1/*
2 * Copyright (c) 1998, 2002, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @summary sanity test for log sudden-death and recovery
26 * @run ignore Requires special hooks in ReliableLog not yet putback.
27 */
28
29/* The ReliableLog uses three filenames and renaming to effect atomic
30 * versionFile updates.  First, a newVersionFile (an intention list)
31 * is written.  Next, the current versionFile is renamed to an
32 * oldVersionFile (an undo list).  Finally, the newVersionFile is
33 * renamed to the current versionFile, and the undo list is discarded.
34 * If the current version file does not exist on restart, then
35 * stability can always be restored by reading the oldVersionFile.
36 *
37 * This test uses little conditional bombs.  When a switch is flipped
38 * in ReliableLog, the code will abort with an InternalError at a
39 * particular point.  We then pretend to have come up from scratch and
40 * recover from the bombed situation.
41 */
42
43import java.io.File;
44import java.io.IOException;
45import java.io.PrintStream;
46import java.io.Serializable;
47import sun.rmi.log.LogHandler;
48import sun.rmi.log.ReliableLog;
49
50//import javasoft.sqe.harness.Status;
51//import javasoft.sqe.harness.Test;
52
53public class Recovery
54    extends LogHandler
55    implements Serializable //, Test
56{
57    static private final String dir = "./recovery_tmp";
58
59    static public void main (String[] argv)
60    {
61        Recovery test = new Recovery();
62        //Status status = test.run (argv, System.err, System.out);
63        //status.exit();
64        test.run (argv, System.err, System.out);
65    }
66
67    //public Status run (String argv[], PrintStream log, PrintStream out)
68    public void run (String argv[], PrintStream lg, PrintStream out)
69    {
70        try {
71            int size;
72            int deathpoint;
73            for (size = 1; size < 256; size *= 2) {
74                for (deathpoint = 0; deathpoint <= 8; deathpoint++) {
75                    // Trash the log directory
76                    try {
77                        (new File(dir,"Version_Number")).delete();
78                    } catch (Exception e) {
79                    }
80                    try {
81                        (new File(dir,"New_Version_Number")).delete();
82                    } catch (Exception e) {
83                    }
84                    try {
85                        (new File(dir,"Old_Version_Number")).delete();
86                    } catch (Exception e) {
87                    }
88
89                    Recovery handler = new Recovery();
90                    ReliableLog log;
91                    if (size == 4 && deathpoint == 6) {
92                        Runtime.getRuntime().traceMethodCalls(true);
93                    }
94                    log = new ReliableLog(dir, handler);
95                    if (size == 4 && deathpoint == 6) {
96                        Runtime.getRuntime().traceMethodCalls(false);
97                    }
98
99                    // Generate a number of updates (size - 1) until failing
100                    int i;
101                    StringBuffer writ = new StringBuffer();
102                    char[] u = new char[1];
103                    for (i = 1; i < size; i++) {
104                        u[0] = (char)(64 + (i % 26));
105                        String update = new String(u);
106                        handler.basicUpdate(update);
107                        log.update(update, true);
108                        writ.append(update);
109                    }
110
111                    // Fail
112                    String f = ("FALL" + deathpoint);
113                    boolean failed_as_desired = false;
114                    try {
115                        ReliableLog.fallOverPoint = deathpoint;
116                        handler.basicUpdate(f);
117                        log.update(f, true);
118                        log.snapshot(handler);
119                    } catch (InternalError e) {
120                        if (!e.getMessage().equals(f))
121                            throw e; // oops, not ours
122                        failed_as_desired = true;
123                    } finally {
124                        ReliableLog.fallOverPoint = 0;
125                        try {
126                            log.close();
127                        } catch (IOException ign) {
128                        }
129                    }
130
131                    // deathpoint == 0 means that there is no deathpoint and we are testing
132                    // undisastered behaviour.
133                    if (!failed_as_desired && deathpoint != 0) {
134                        System.err.println ("sun.rmi.log.ReliableLog is not instrumented for" +
135                                            " this test; test skipped");
136                        return;
137                    }
138
139                    // Now try to recover.
140                    Recovery laz = new Recovery();
141                    ReliableLog carbon = new ReliableLog(dir, laz);
142                    Recovery thingy;
143                    thingy = (Recovery)carbon.recover();
144                    try {
145                        carbon.close();
146                    } catch (IOException ign) {
147                    }
148
149                    // Recovered thingy must be equal to writ or to writ + f, but nothing
150                    // else (or in between).
151                    String recovered = thingy.contents;
152                    String sacr = writ.toString();
153                    if (recovered.length() == sacr.length()
154                        && recovered.compareTo(sacr) == 0)
155                    {
156                        lg.println("Passed test " + size + "/" + deathpoint
157                                   + ": rolled back to v1");
158                    } else if (recovered.length() == (sacr.length() + f.length())
159                               && recovered.compareTo(sacr + f) == 0)
160                    {
161                        lg.println("Passed test " + size + "/" + deathpoint
162                                   + ": committed to v2");
163                    } else {
164                        final String q = "\"";
165                        lg.println("Wrote " + sacr.length() + ": " + q + sacr + q);
166                        lg.println("Simulated failure during write "
167                                   + f.length() + ": " + q + f + q);
168                        lg.println("Recovered " + recovered.length() + ": " + q + recovered + q);
169                        throw new InternalError("Failed test " + size + "/" + deathpoint
170                                   + " (size/deathpoint):");
171                    }
172                }
173            }
174        } catch (Exception e) {
175            e.printStackTrace(lg);
176            //return (Status.failed
177            throw (new RuntimeException
178                    ("Exception in sanity test for sun.rmi.log.ReliableLog"));
179        }
180        //return (Status.passed ("OKAY"));
181    }
182
183    private String contents;
184    transient private StringBuffer trc = null;
185
186    public Recovery()
187    {
188        super();
189        if (this.contents == null)
190            this.contents = "?";
191    }
192
193    // implements LogHandler.initialSnapshot()
194    public Object initialSnapshot()
195        throws Exception
196    {
197        this.contents = "";
198        return (this);
199    }
200
201    // implements LogHandler.applyUpdate()
202    public Object applyUpdate (Object update, Object state)
203        throws Exception
204    {
205        // basicUpdate appends the string
206        ((Recovery)state).basicUpdate ((String)update);
207        return (state);
208    }
209
210    // an "update" is a short string to append to this.contents (must ignore state)
211    public void basicUpdate (String extra)
212    {
213        this.contents = this.contents + extra;
214    }
215}
216