• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/db-4.7.25.NC/docs/gsg_db_rep/JAVA/
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>Example Processing Loop</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="fwrkmasterreplica.html" title="Chapter��4.��Replica versus Master Processes" />
11    <link rel="previous" href="processingloop.html" title="Processing Loop" />
12    <link rel="next" href="addfeatures.html" title="Chapter��5.��Additional Features" />
13  </head>
14  <body>
15    <div class="navheader">
16      <table width="100%" summary="Navigation header">
17        <tr>
18          <th colspan="3" align="center">Example Processing Loop</th>
19        </tr>
20        <tr>
21          <td width="20%" align="left"><a accesskey="p" href="processingloop.html">Prev</a>��</td>
22          <th width="60%" align="center">Chapter��4.��Replica versus Master Processes</th>
23          <td width="20%" align="right">��<a accesskey="n" href="addfeatures.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="exampledoloop"></a>Example Processing Loop</h2>
33          </div>
34        </div>
35        <div></div>
36      </div>
37      <p>
38                          In this section we take the example
39                          processing loop that we presented in the
40                          previous section and we flesh it out to
41                          provide a more complete example. We do this
42                          by updating the
43                          <tt class="function">doloop()</tt>
44                          function that our original transaction
45                          application used 
46                          
47                          
48                          <span>(see <a href="simpleprogramlisting.html#doloop_java">Method: SimpleTxn.doloop()</a>)</span>
49                          to fully support our replicated application. 
50                  </p>
51      <p>
52                          In the following example code, code that we
53                          add to the original example is presented in 
54                          <b class="userinput"><tt>bold</tt></b>.
55                  </p>
56      <p>
57                        To begin, we must implement a way to track whether
58                        our application is running as a master or a client.
59                        There are many ways to do this, but in this case
60                        what we will do is extend
61                        <tt class="classname">com.sleepycat.db.Environment</tt>
62                        to carry the information. We do this by creating
63                        the <tt class="classname">RepQuoteEnvironment</tt>
64                        class.
65                   </p>
66      <pre class="programlisting">
67        <b class="userinput">
68          <tt>package db.repquote;
69
70import com.sleepycat.db.DatabaseException;
71import com.sleepycat.db.Environment;
72import com.sleepycat.db.EnvironmentConfig;
73
74public class RepQuoteEnvironment extends Environment
75{
76    private boolean isMaster;
77
78    public RepQuoteEnvironment(final java.io.File host,
79        EnvironmentConfig config)
80        throws DatabaseException, java.io.FileNotFoundException
81    {
82        super(host, config);
83        isMaster = false;
84    }
85
86    boolean getIsMaster()
87    {
88        return isMaster;
89    }
90
91    public void setIsMaster(boolean isMaster)
92    {
93        this.isMaster = isMaster;
94    }
95} </tt>
96        </b>
97      </pre>
98      <p>
99                          Next, we go to <tt class="filename">RepQuoteExample.java</tt> and
100                          we include the
101                          <tt class="classname">RepQuoteEnvironment</tt> class
102                          as well as the
103                          <tt class="classname">EventHandler</tt> class. We then
104                          cause our <tt class="classname">RepQuoteExample</tt>
105                          class to implement
106                          <tt class="classname">EventHandler</tt>. We also
107                          change our environment handle to be an instance
108                          of <tt class="classname">RepQuoteEnvironment</tt>
109                          instead of <tt class="classname">Environment</tt>.
110                  </p>
111      <p>
112                          Note that we also import the
113                          <tt class="classname">com.sleepycat.db.ReplicationHandleDeadException</tt>
114                          class. We will discuss what that exception is
115                          used for a little later in this example.
116                  </p>
117      <pre class="programlisting">package db.repquote;
118
119import java.io.FileNotFoundException;
120import java.io.BufferedReader;
121import java.io.InputStreamReader;
122import java.io.IOException;
123import java.io.UnsupportedEncodingException;
124import java.lang.Thread;
125import java.lang.InterruptedException;
126
127import com.sleepycat.db.Cursor;
128import com.sleepycat.db.Database;
129import com.sleepycat.db.DatabaseConfig;
130import com.sleepycat.db.DatabaseEntry;
131import com.sleepycat.db.DatabaseException;
132import com.sleepycat.db.DatabaseType;
133import com.sleepycat.db.EnvironmentConfig;
134<b class="userinput"><tt>import com.sleepycat.db.EventHandler;</tt></b>
135import com.sleepycat.db.LockMode;
136import com.sleepycat.db.OperationStatus;
137<b class="userinput"><tt>import com.sleepycat.db.ReplicationHandleDeadException;</tt></b>
138
139import db.repquote.RepConfig;
140<b class="userinput"><tt>import db.repquote.RepQuoteEnvironment</tt></b>
141
142public class RepQuoteExample <b class="userinput"><tt> implements EventHandler</tt></b>
143{
144    private RepConfig repConfig;
145    private <b class="userinput"><tt>RepQuoteEnvironment</tt></b> dbenv; </pre>
146      <p>
147        That done, we can skip the 
148        
149        
150        <span><tt class="methodname">main()</tt> method and
151                our class constructor, because they do not change.</span>
152        
153        Instead, we skip down to our
154        
155
156        <span><tt class="methodname">init()</tt> method
157                where we take care of opening our environment and setting
158                the event handler.  </span>
159</p>
160      <p>
161        To update our <tt class="methodname">init()</tt> method, we only need
162        to do a couple of things. First, we identify the current class as
163        the event handler. Then, when we open our environment, we
164        instantiate a <tt class="classname">RepQuoteEnvironment</tt>
165        class instead of an <tt class="classname">Environment</tt>
166        class.
167</p>
168      <pre class="programlisting">    public int init(RepConfig config)
169        throws DatabaseException
170    {
171        int ret = 0;
172        appConfig = config;
173        EnvironmentConfig envConfig = new EnvironmentConfig();
174        envConfig.setErrorStream(System.err);
175        envConfig.setErrorPrefix(RepConfig.progname);
176
177        envConfig.setReplicationManagerLocalSite(appConfig.getThisHost());
178        for (ReplicationHostAddress host = appConfig.getFirstOtherHost();
179            host != null; host = appConfig.getNextOtherHost())
180        {
181            envConfig.replicationManagerAddRemoteSite(host);
182        }
183
184        if (appConfig.totalSites &gt; 0)
185            envConfig.setReplicationNumSites(appConfig.totalSites);
186        envConfig.setReplicationPriority(appConfig.priority);
187
188        envConfig.setReplicationManagerAckPolicy(
189            ReplicationManagerAckPolicy.ALL);
190        envConfig.setCacheSize(RepConfig.CACHESIZE);
191        envConfig.setTxnNoSync(true);
192
193        <b class="userinput"><tt>envConfig.setEventHandler(this);</tt></b>
194
195        envConfig.setAllowCreate(true);
196        envConfig.setRunRecovery(true);
197        envConfig.setThreaded(true);
198        envConfig.setInitializeReplication(true);
199        envConfig.setInitializeLocking(true);
200        envConfig.setInitializeLogging(true);
201        envConfig.setInitializeCache(true);
202        envConfig.setTransactional(true);
203        envConfig.setVerboseReplication(appConfig.verbose);
204        try {
205            dbenv = new <b class="userinput"><tt>RepQuoteEnvironment</tt></b>(appConfig.getHome(), envConfig);
206        } catch(FileNotFoundException e) {
207            System.err.println("FileNotFound exception: " + e.toString());
208            System.err.println(
209                "Ensure that the environment directory is pre-created.");
210            ret = 1;
211        }
212
213        // start replication manager
214        dbenv.replicationManagerStart(3, appConfig.startPolicy);
215        return ret;
216    } </pre>
217      <p>
218        That done, we need to implement our
219        <tt class="methodname">handleEvent()</tt> method. 
220        This method is required because we are now implementing 
221        <tt class="classname">com.sleepycat.db.EventHandler</tt>. We use this
222        method to track whether we are operating as a master.
223</p>
224      <pre class="programlisting">    public int handleEvent(EventType event)
225    {
226        int ret = 0;
227        if (event == EventType.REP_MASTER)
228            dbenv.setIsMaster(true);
229        else if (event == EventType.REP_CLIENT)
230            dbenv.setIsMaster(false);
231        else if (event == EventType.REP_NEW_MASTER) {
232            // ignored for now.
233        } else {
234            System.err.println("Unknown event callback received.\n");
235            ret = 1;
236        }
237        return ret;
238    } </pre>
239      <p>
240        That done, we need to update our
241        <tt class="function">doloop()</tt> 
242        
243
244        <span>method.</span>
245</p>
246      <p>
247            We begin by updating our <tt class="classname">DatabaseConfig</tt>
248            instance to determine which options to use, depending on whether the
249            application is running as a master.
250    </p>
251      <pre class="programlisting">    public int doloop()
252        throws DatabaseException
253    {
254        Database db = null;
255
256        for (;;)
257        {
258            if (db == null) {
259                DatabaseConfig dbconf = new DatabaseConfig();
260                // Set page size small so page allocation is cheap.
261                dbconf.setPageSize(512);
262                dbconf.setType(DatabaseType.BTREE);
263                <b class="userinput"><tt>if (dbenv.getIsMaster()) {
264                    dbconf.setAllowCreate(true);
265                }</tt></b>
266                dbconf.setTransactional(true); </pre>
267      <p>
268    When we open the database, we modify our error handling to
269    account for the case where the database does not yet exist. This can
270    happen if our code is running as a replica and the replication framework has not
271    yet had a chance to create the databases for us. Recall that replicas never
272    write to their own databases directly, and so they cannot
273    create databases on their own.
274</p>
275      <p>
276        If we detect that the database does not yet exist, we simply
277        close the database handle, sleep for a short period of time
278        and then continue processing. This gives the replication framework a chance to
279        create the database so that our replica can continue
280        operations.
281</p>
282      <pre class="programlisting">                try {
283                    db = dbenv.openDatabase
284                        (null, RepConfig.progname, null, dbconf);
285                } catch (java.io.FileNotFoundException e) {
286                    <b class="userinput"><tt>System.err.println("no stock database available yet.");
287                    if (db != null) {
288                        db.close(true);
289                        db = null;
290                    }
291                    try {
292                        Thread.sleep(RepConfig.SLEEPTIME);
293                    } catch (InterruptedException ie) {}
294                    continue;</tt></b>
295                }
296            } </pre>
297      <p>
298        Next we modify our prompt, so that if the local process is running
299        as a replica, we can tell from the shell that the prompt is for a
300        read-only process.
301    </p>
302      <pre class="programlisting">            BufferedReader stdin =
303                new BufferedReader(new InputStreamReader(System.in));
304
305            // listen for input, and add it to the database.
306            System.out.print("QUOTESERVER");
307            <b class="userinput"><tt>if (!dbenv.getIsMaster())
308                System.out.print("(read-only)");
309            System.out.print("&gt; ");</tt></b>
310            System.out.flush();
311            String nextline = null;
312            try {
313                nextline = stdin.readLine();
314            } catch (IOException ioe) {
315                System.err.println("Unable to get data from stdin");
316                break;
317            }
318            String[] words = nextline.split("\\s"); </pre>
319      <p>
320            When we collect data from the prompt, there is a case that says
321            if no data is entered then show the entire stocks database.
322            This display is performed by our
323            <tt class="function">print_stocks()</tt> 
324             
325            <span>method</span> 
326            (which has not
327            required a modification since we first introduced it in 
328            <a href="simpleprogramlisting.html#printstocks_c">
329                            
330                            
331                            <span>Method: SimpleTxn.printStocks()</span>
332                    </a>).
333    </p>
334      <p>
335            When we call 
336               
337               <span><tt class="function">printStocks()</tt>, </span>
338            we check for a dead replication handle. Dead
339            replication handles happen whenever a replication election
340            results in a previously committed transaction becoming
341            invalid. This is an error scenario caused by a new master having a
342            slightly older version of the data than the original
343            master and so all replicas must modify their database(s) to
344            reflect that of the new master. In this situation, some
345            number of previously committed transactions may have to be
346            unrolled. From the replica's perspective, the database
347            handles should all be closed and then opened again.
348    </p>
349      <pre class="programlisting">            // A blank line causes the DB to be dumped to stdout.
350            if (words.length == 0 ||
351                (words.length == 1 &amp;&amp; words[0].length() == 0)) {
352                try {
353                    printStocks(db);
354                <b class="userinput"><tt>} catch (DeadlockException de) {
355                    continue;
356                // Dead replication handles are caused by an election
357                // resulting in a previously committing read becoming
358                // invalid. Close the db handle and reopen.
359                } catch (ReplicationHandleDeadException rhde) {
360                    db.close(true); // close no sync.
361                    db = null;
362                    continue;</tt></b>
363                } catch (DatabaseException e) {
364                    System.err.println("Got db exception reading " +
365                        "replication DB: " + e.toString());
366                    break;
367                }
368                continue;
369            }
370
371            if (words.length == 1 &amp;&amp;
372                (words[0].compareToIgnoreCase("quit") == 0 ||
373                words[0].compareToIgnoreCase("exit") == 0)) {
374                break;
375            } else if (words.length != 2) {
376                System.err.println("Format: TICKER VALUE");
377                continue;
378            } </pre>
379      <p>
380            That done, we need to add a little error checking to our
381            command prompt to make sure the user is not attempting to
382            modify the database at a replica. Remember, replicas must never
383            modify their local databases on their own. This guards against
384            that happening due to user input at the prompt.
385    </p>
386      <pre class="programlisting">            <b class="userinput"><tt>if (!dbenv.getIsMaster()) {
387                System.err.println("Can't update client.");
388                continue;
389            }</tt></b>
390
391            DatabaseEntry key = new DatabaseEntry(words[0].getBytes());
392            DatabaseEntry data = new DatabaseEntry(words[1].getBytes());
393
394            db.put(null, key, data);
395        }
396        if (db != null)
397            db.close(true);
398        return 0;
399    } </pre>
400      <p>
401        With that completed, we are all done updating our application
402        for replication. 
403
404        The only remaining 
405         
406         
407        <span>method, <tt class="function">printStocks()</tt>,</span> 
408
409        is unmodified from when we
410        originally introduced it. For details on that function, see
411        <a href="simpleprogramlisting.html#printstocks_c">
412                            
413                            
414                            <span>Method: SimpleTxn.printStocks()</span>
415                    </a>.
416</p>
417      <div class="sect2" lang="en" xml:lang="en">
418        <div class="titlepage">
419          <div>
420            <div>
421              <h3 class="title"><a id="runningit"></a>Running It</h3>
422            </div>
423          </div>
424          <div></div>
425        </div>
426        <p>
427                To run our replicated application, we need to make
428                sure each participating environment has its own unique
429                home directory. We can do this by running
430                each site on a separate networked machine, but that
431                is not strictly necessary; multiple instances of this
432                code can run on the same machine provided the
433                environment home restriction is observed.
434        </p>
435        <p>
436                To run a process, make sure the environment home
437                exists and then start the process using the
438                <tt class="literal">-h</tt> option to specify that
439                directory. You must also use the <tt class="literal">-m</tt>
440                option to identify the local host and port that this
441                process will use to listen for replication messages, and
442                the <tt class="literal">-o</tt> option to identify the other
443                processes in the replication group. Finally, use the
444                <tt class="literal">-p</tt> option to specify a priority.
445                The process that you designate to have the highest priority will become
446                the master.
447        </p>
448        <pre class="programlisting">&gt; mkdir env1
449&gt; java db.repquote.RepQuoteExample -h env1 -n 2 -m localhost:8080 \
450-o localhost:8081 -p 10
451No stock database yet available.
452No stock database yet available.  </pre>
453        <p>
454        Now, start another process. This time, change the environment
455        home to something else, use the <tt class="literal">-m</tt> to at
456        least change the port number the process is listening on, and
457        use the <tt class="literal">-o</tt> option to identify the host and
458        port of the other replication process:
459</p>
460        <pre class="programlisting">&gt; mkdir env2
461&gt; java db.repquote.RepQuoteExample -h env2 -n 2 -m localhost:8081 \
462-o localhost:8080 -p 20</pre>
463        <p>
464        After a short pause, the second process should display the master
465        prompt:
466</p>
467        <pre class="programlisting">
468QUOTESERVER &gt; </pre>
469        <p>
470        And the first process should 
471        display the read-only prompt:
472</p>
473        <pre class="programlisting">
474QUOTESERVER (read-only)&gt; </pre>
475        <p>
476        Now go to the master process and give it a couple of stocks and stock
477        prices:
478</p>
479        <pre class="programlisting">QUOTESERVER&gt; FAKECO 9.87
480QUOTESERVER&gt; NOINC .23
481QUOTESERVER&gt; </pre>
482        <p>
483    Then, go to the replica and hit <b class="userinput"><tt>return</tt></b> at the prompt to
484    see the new values:
485</p>
486        <pre class="programlisting">QUOTESERVER (read-only)&gt; 
487        Symbol  Price
488        ======  =====
489        FAKECO  9.87
490        NOINC    .23 
491QUOTESERVER (read-only)&gt; </pre>
492        <p>
493        Doing the same at the master results in the same thing:
494</p>
495        <pre class="programlisting">QUOTESERVER&gt; 
496        Symbol  Price
497        ======  =====
498        FAKECO  9.87
499        NOINC    .23 
500QUOTESERVER&gt; </pre>
501        <p>
502        You can change a stock by simply entering the stock value and
503        new price at the master's prompt:
504</p>
505        <pre class="programlisting">QUOTESERVER&gt; FAKECO 10.01 
506QUOTESERVER&gt; </pre>
507        <p>
508        Then, go to either the master or the replica to see the updated
509        database:
510</p>
511        <pre class="programlisting">QUOTESERVER&gt; 
512        Symbol  Price
513        ======  =====
514        FAKECO  10.01
515        NOINC    .23 
516QUOTESERVER&gt; </pre>
517        <p>
518        And on the replica:
519</p>
520        <pre class="programlisting">QUOTESERVER (read-only)&gt; 
521        Symbol  Price
522        ======  =====
523        FAKECO  10.01
524        NOINC    .23 
525QUOTESERVER (read-only)&gt; </pre>
526        <p>
527        Finally, to quit the applications, simply type
528        <tt class="literal">quit</tt> at both prompts:
529</p>
530        <pre class="programlisting">QUOTESERVER (read-only)&gt; quit
531&gt; </pre>
532        <p>
533        And on the master as well:
534</p>
535        <pre class="programlisting">QUOTESERVER&gt; quit
536&gt; </pre>
537      </div>
538    </div>
539    <div class="navfooter">
540      <hr />
541      <table width="100%" summary="Navigation footer">
542        <tr>
543          <td width="40%" align="left"><a accesskey="p" href="processingloop.html">Prev</a>��</td>
544          <td width="20%" align="center">
545            <a accesskey="u" href="fwrkmasterreplica.html">Up</a>
546          </td>
547          <td width="40%" align="right">��<a accesskey="n" href="addfeatures.html">Next</a></td>
548        </tr>
549        <tr>
550          <td width="40%" align="left" valign="top">Processing Loop��</td>
551          <td width="20%" align="center">
552            <a accesskey="h" href="index.html">Home</a>
553          </td>
554          <td width="40%" align="right" valign="top">��Chapter��5.��Additional Features</td>
555        </tr>
556      </table>
557    </div>
558  </body>
559</html>
560