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