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 
  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 }