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
36import sun.rmi.log.LogHandler;
37import sun.rmi.log.ReliableLog;
38import java.io.FileDescriptor;
39import java.io.FileNotFoundException;
40import java.io.IOException;
41import java.io.Serializable;
42
43public 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
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
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
132        Counter() {}
133
134        int increment() {
135            return ++count;
136        }
137
138        int value() {
139            return count;
140        }
141
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
156        TestLogHandler(Counter initialState) {
157            this.initialState = initialState;
158        }
159
160        public Object initialSnapshot() {
161            if (initialState == null) {
162                throw new IllegalStateException(
163                    "attempting initialSnapshot with null");
164            }
165            return initialState;
166        }
167
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
176        public MyLogFile(String name, String mode)
177            throws FileNotFoundException, IOException
178        {
179            super(name, mode);
180        }
181
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
191        protected boolean checkSpansBoundary(long fp) {
192            return
193                crashPoint > 0 ? spansBoundary : super.checkSpansBoundary(fp);
194        }
195    }
196}
197