| /* |
| * Copyright (c) 1998, 2002, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @summary sanity test for log sudden-death and recovery |
| * @run ignore Requires special hooks in ReliableLog not yet putback. |
| */ |
| |
| /* The ReliableLog uses three filenames and renaming to effect atomic |
| * versionFile updates. First, a newVersionFile (an intention list) |
| * is written. Next, the current versionFile is renamed to an |
| * oldVersionFile (an undo list). Finally, the newVersionFile is |
| * renamed to the current versionFile, and the undo list is discarded. |
| * If the current version file does not exist on restart, then |
| * stability can always be restored by reading the oldVersionFile. |
| * |
| * This test uses little conditional bombs. When a switch is flipped |
| * in ReliableLog, the code will abort with an InternalError at a |
| * particular point. We then pretend to have come up from scratch and |
| * recover from the bombed situation. |
| */ |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.io.Serializable; |
| import sun.rmi.log.LogHandler; |
| import sun.rmi.log.ReliableLog; |
| |
| //import javasoft.sqe.harness.Status; |
| //import javasoft.sqe.harness.Test; |
| |
| public class Recovery |
| extends LogHandler |
| implements Serializable //, Test |
| { |
| static private final String dir = "./recovery_tmp"; |
| |
| static public void main (String[] argv) |
| { |
| Recovery test = new Recovery(); |
| //Status status = test.run (argv, System.err, System.out); |
| //status.exit(); |
| test.run (argv, System.err, System.out); |
| } |
| |
| //public Status run (String argv[], PrintStream log, PrintStream out) |
| public void run (String argv[], PrintStream lg, PrintStream out) |
| { |
| try { |
| int size; |
| int deathpoint; |
| for (size = 1; size < 256; size *= 2) { |
| for (deathpoint = 0; deathpoint <= 8; deathpoint++) { |
| // Trash the log directory |
| try { |
| (new File(dir,"Version_Number")).delete(); |
| } catch (Exception e) { |
| } |
| try { |
| (new File(dir,"New_Version_Number")).delete(); |
| } catch (Exception e) { |
| } |
| try { |
| (new File(dir,"Old_Version_Number")).delete(); |
| } catch (Exception e) { |
| } |
| |
| Recovery handler = new Recovery(); |
| ReliableLog log; |
| if (size == 4 && deathpoint == 6) { |
| Runtime.getRuntime().traceMethodCalls(true); |
| } |
| log = new ReliableLog(dir, handler); |
| if (size == 4 && deathpoint == 6) { |
| Runtime.getRuntime().traceMethodCalls(false); |
| } |
| |
| // Generate a number of updates (size - 1) until failing |
| int i; |
| StringBuffer writ = new StringBuffer(); |
| char[] u = new char[1]; |
| for (i = 1; i < size; i++) { |
| u[0] = (char)(64 + (i % 26)); |
| String update = new String(u); |
| handler.basicUpdate(update); |
| log.update(update, true); |
| writ.append(update); |
| } |
| |
| // Fail |
| String f = ("FALL" + deathpoint); |
| boolean failed_as_desired = false; |
| try { |
| ReliableLog.fallOverPoint = deathpoint; |
| handler.basicUpdate(f); |
| log.update(f, true); |
| log.snapshot(handler); |
| } catch (InternalError e) { |
| if (!e.getMessage().equals(f)) |
| throw e; // oops, not ours |
| failed_as_desired = true; |
| } finally { |
| ReliableLog.fallOverPoint = 0; |
| try { |
| log.close(); |
| } catch (IOException ign) { |
| } |
| } |
| |
| // deathpoint == 0 means that there is no deathpoint and we are testing |
| // undisastered behaviour. |
| if (!failed_as_desired && deathpoint != 0) { |
| System.err.println ("sun.rmi.log.ReliableLog is not instrumented for" + |
| " this test; test skipped"); |
| return; |
| } |
| |
| // Now try to recover. |
| Recovery laz = new Recovery(); |
| ReliableLog carbon = new ReliableLog(dir, laz); |
| Recovery thingy; |
| thingy = (Recovery)carbon.recover(); |
| try { |
| carbon.close(); |
| } catch (IOException ign) { |
| } |
| |
| // Recovered thingy must be equal to writ or to writ + f, but nothing |
| // else (or in between). |
| String recovered = thingy.contents; |
| String sacr = writ.toString(); |
| if (recovered.length() == sacr.length() |
| && recovered.compareTo(sacr) == 0) |
| { |
| lg.println("Passed test " + size + "/" + deathpoint |
| + ": rolled back to v1"); |
| } else if (recovered.length() == (sacr.length() + f.length()) |
| && recovered.compareTo(sacr + f) == 0) |
| { |
| lg.println("Passed test " + size + "/" + deathpoint |
| + ": committed to v2"); |
| } else { |
| final String q = "\""; |
| lg.println("Wrote " + sacr.length() + ": " + q + sacr + q); |
| lg.println("Simulated failure during write " |
| + f.length() + ": " + q + f + q); |
| lg.println("Recovered " + recovered.length() + ": " + q + recovered + q); |
| throw new InternalError("Failed test " + size + "/" + deathpoint |
| + " (size/deathpoint):"); |
| } |
| } |
| } |
| } catch (Exception e) { |
| e.printStackTrace(lg); |
| //return (Status.failed |
| throw (new RuntimeException |
| ("Exception in sanity test for sun.rmi.log.ReliableLog")); |
| } |
| //return (Status.passed ("OKAY")); |
| } |
| |
| private String contents; |
| transient private StringBuffer trc = null; |
| |
| public Recovery() |
| { |
| super(); |
| if (this.contents == null) |
| this.contents = "?"; |
| } |
| |
| // implements LogHandler.initialSnapshot() |
| public Object initialSnapshot() |
| throws Exception |
| { |
| this.contents = ""; |
| return (this); |
| } |
| |
| // implements LogHandler.applyUpdate() |
| public Object applyUpdate (Object update, Object state) |
| throws Exception |
| { |
| // basicUpdate appends the string |
| ((Recovery)state).basicUpdate ((String)update); |
| return (state); |
| } |
| |
| // an "update" is a short string to append to this.contents (must ignore state) |
| public void basicUpdate (String extra) |
| { |
| this.contents = this.contents + extra; |
| } |
| } |