1 /*
2  * Copyright (c) 2005, 2012, 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  * @test
26  * @bug 4652922
27  *
28  * @summary synopsis: ReliableLog.update should pad records to 4-byte
29  * boundaries
30  * @author Ann Wollrath
31  *
32  * @run main/othervm/timeout=240 LogTest
33  */
34 
35 import sun.rmi.log.LogHandler;
36 import sun.rmi.log.ReliableLog;
37 import java.io.FileDescriptor;
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.io.Serializable;
41 
42 public class LogTest {
43 
44     private static int crashPoint = 0;
45     private static boolean spansBoundary = false;
46     private static ReliableLog log =  null;
47     private static Counter counter = new Counter();
48 
main(String[] args)49     public static void main(String[] args) throws Exception {
50 
51         System.err.println("\nRegression test for bug 4652922\n");
52 
53         System.setProperty("sun.rmi.log.class", MyLogFile.class.getName());
54         //System.setProperty("sun.rmi.log.debug", "true");
55 
56         log = new ReliableLog(".", new TestLogHandler(counter), false);
57 
58         writeUpdatesCrashAndRecover(10, 1, false, 10);
59         writeUpdatesCrashAndRecover(10, 1, true, 20);
60         writeUpdatesCrashAndRecover(10, 2, true, 30);
61         writeUpdatesCrashAndRecover(9, 2, false, 40);
62         writeUpdatesCrashAndRecover(9, 3, true, 50);
63         log.close();
64     }
65 
writeUpdatesCrashAndRecover(int updates, int crashValue, boolean spans, int expectedCount)66     private static void writeUpdatesCrashAndRecover(int updates,
67                                                     int crashValue,
68                                                     boolean spans,
69                                                     int expectedCount)
70         throws IOException
71     {
72         /*
73          * Write updates
74          */
75         System.err.println("\nwrite updates: " + updates);
76         for (int i = 0; i < updates; i++) {
77             counter.increment();
78             log.update(counter);
79         }
80 
81         /*
82          * Crash
83          */
84         crashPoint = crashValue;
85         spansBoundary = spans;
86         System.err.println("crash during next update on sync #" +
87                            crashPoint +
88                            " (spansBoundary = " + spansBoundary + ")");
89         try {
90             System.err.println(
91                 "write one update (update record should " +
92                 ((counter.value() + 1 == expectedCount) ? "" : "not ") +
93                 "be complete)");
94             counter.increment();
95             log.update(counter);
96             throw new RuntimeException("failed to reach crashpoint " +
97                                        crashPoint);
98         } catch (IOException e) {
99             System.err.println("caught IOException; message: " +
100                                e.getMessage());
101             log.close();
102         }
103 
104         /*
105          * Recover
106          */
107         log = new ReliableLog(".", new TestLogHandler(null), false);
108         try {
109             System.err.println("recover log");
110             counter = (Counter) log.recover();
111             System.err.println("recovered counter value: " + counter.value());
112             if (counter.value() != expectedCount) {
113                 throw new RuntimeException("unexpected counter value " +
114                                            counter.value());
115             }
116             System.err.println("log recovery successful");
117 
118         } catch (IOException e) {
119             System.err.println("log should recover after crash point");
120             e.printStackTrace();
121             throw new RuntimeException(
122                 "log should recover after crash point");
123         }
124 
125     }
126 
127     private static class Counter implements Serializable {
128         private static long serialVersionUID = 1;
129         private int count = 0;
130 
Counter()131         Counter() {}
132 
increment()133         int increment() {
134             return ++count;
135         }
136 
value()137         int value() {
138             return count;
139         }
140 
update(Counter value)141         void update(Counter value) {
142             if (value.value() < count) {
143                 throw new IllegalStateException(
144                     "bad update (count = " + count + ", value = " + value + ")");
145             } else {
146                 count = value.value();
147             }
148         }
149     }
150 
151     static class TestLogHandler extends LogHandler {
152 
153         private final Counter initialState;
154 
TestLogHandler(Counter initialState)155         TestLogHandler(Counter initialState) {
156             this.initialState = initialState;
157         }
158 
initialSnapshot()159         public Object initialSnapshot() {
160             if (initialState == null) {
161                 throw new IllegalStateException(
162                     "attempting initialSnapshot with null");
163             }
164             return initialState;
165         }
166 
applyUpdate(Object update, Object state)167         public Object applyUpdate(Object update, Object state) {
168             ((Counter) state).update((Counter) update);
169             return state;
170         }
171     }
172 
173     public static class MyLogFile extends ReliableLog.LogFile {
174 
MyLogFile(String name, String mode)175         public MyLogFile(String name, String mode)
176             throws FileNotFoundException, IOException
177         {
178             super(name, mode);
179         }
180 
sync()181         protected void sync() throws IOException {
182             if (crashPoint != 0) {
183                 if (--crashPoint == 0) {
184                     throw new IOException("crash point reached");
185                 }
186             }
187             super.sync();
188         }
189 
checkSpansBoundary(long fp)190         protected boolean checkSpansBoundary(long fp) {
191             return
192                 crashPoint > 0 ? spansBoundary : super.checkSpansBoundary(fp);
193         }
194     }
195 }
196