001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (license2)
004: * Initial Developer: H2 Group
005: */
006: package org.h2.test.synth;
007:
008: import java.io.File;
009: import java.io.FileWriter;
010: import java.io.IOException;
011: import java.io.InputStream;
012: import java.io.PrintWriter;
013: import java.sql.Connection;
014: import java.sql.DriverManager;
015: import java.sql.SQLException;
016: import java.text.SimpleDateFormat;
017: import java.util.Date;
018: import java.util.Random;
019:
020: import org.h2.test.TestAll;
021: import org.h2.test.TestBase;
022: import org.h2.test.unit.SelfDestructor;
023: import org.h2.tools.Backup;
024: import org.h2.tools.DeleteDbFiles;
025: import org.h2.util.IOUtils;
026:
027: /**
028: * Tests database recovery by destroying a process that writes to the database.
029: */
030: public abstract class TestHalt extends TestBase {
031:
032: private SimpleDateFormat dateFormat = new SimpleDateFormat(
033: "MM-dd HH:mm:ss ");
034: protected static final int OP_INSERT = 1, OP_DELETE = 2,
035: OP_UPDATE = 4, OP_SELECT = 8;
036: protected static final int FLAG_NO_DELAY = 1, FLAG_LOBS = 2;
037: protected int operations, flags, value;
038: protected Connection conn;
039: protected Random random = new Random();
040: private int errorId;
041: private int sequenceId;
042: private static final String DATABASE_NAME = "halt";
043: static final String DIR = TestBase.getTestDir("halt");
044: private static final String TRACE_FILE_NAME = "haltTrace.trace.db";
045:
046: abstract void testInit() throws Exception;
047:
048: abstract void testCheckAfterCrash() throws Exception;
049:
050: abstract void testWaitAfterAppStart() throws Exception;
051:
052: abstract void appStart() throws Exception;
053:
054: abstract void appRun() throws Exception;
055:
056: public void test() throws Exception {
057: for (int i = 0;; i++) {
058: operations = OP_INSERT | i;
059: flags = i >> 4;
060: // flags |= FLAG_NO_DELAY; // | FLAG_LOBS;
061: try {
062: runTest();
063: } catch (Throwable t) {
064: System.out.println("Error: " + t);
065: t.printStackTrace();
066: }
067: }
068: }
069:
070: Connection getConnection() throws Exception {
071: Class.forName("org.h2.Driver");
072: return DriverManager.getConnection("jdbc:h2:" + baseDir
073: + "/halt", "sa", "sa");
074: }
075:
076: protected void start(String[] args) throws Exception {
077: if (args.length == 0) {
078: runTest();
079: } else {
080: operations = Integer.parseInt(args[0]);
081: flags = Integer.parseInt(args[1]);
082: value = Integer.parseInt(args[2]);
083: runRandom();
084: }
085: }
086:
087: private void runRandom() throws Exception {
088: connect();
089: try {
090: traceOperation("connected, operations:" + operations
091: + " flags:" + flags + " value:" + value);
092: appStart();
093: System.out.println("READY");
094: System.out.println("READY");
095: System.out.println("READY");
096: appRun();
097: traceOperation("done");
098: } catch (Exception e) {
099: trace("run", e);
100: }
101: disconnect();
102: }
103:
104: private void connect() throws Exception {
105: try {
106: traceOperation("connecting");
107: conn = getConnection();
108: } catch (Exception e) {
109: trace("connect", e);
110: e.printStackTrace();
111: throw e;
112: }
113: }
114:
115: protected void traceOperation(String s) {
116: trace(s, null);
117: }
118:
119: protected void trace(String s, Exception e) {
120: FileWriter writer = null;
121: try {
122: File f = new File(baseDir + "/" + TRACE_FILE_NAME);
123: f.getParentFile().mkdirs();
124: writer = new FileWriter(f, true);
125: PrintWriter w = new PrintWriter(writer);
126: s = dateFormat.format(new Date()) + ": " + s;
127: w.println(s);
128: if (e != null) {
129: e.printStackTrace(w);
130: }
131: } catch (IOException e2) {
132: e2.printStackTrace();
133: } finally {
134: IOUtils.closeSilently(writer);
135: }
136: }
137:
138: private void runTest() throws Exception {
139: traceOperation("delete database -----------------------------");
140: DeleteDbFiles.execute(baseDir, DATABASE_NAME, true);
141: new File(baseDir + "/" + TRACE_FILE_NAME).delete();
142:
143: connect();
144: testInit();
145: disconnect();
146: for (int i = 0; i < 10; i++) {
147: traceOperation("backing up " + sequenceId);
148: Backup.execute(baseDir + "/haltSeq" + sequenceId + ".zip",
149: baseDir, null, true);
150: sequenceId++;
151: // int operations = OP_INSERT;
152: // OP_DELETE = 1, OP_UPDATE = 2, OP_SELECT = 4;
153: // int flags = FLAG_NODELAY;
154: // FLAG_NO_DELAY = 1, FLAG_AUTO_COMMIT = 2, FLAG_SMALL_CACHE = 4;
155: int value = random.nextInt(1000);
156: // for Derby and HSQLDB
157: // String classPath = "-cp
158: // .;D:/data/java/hsqldb.jar;D:/data/java/derby.jar";
159: String selfDestruct = SelfDestructor.getPropertyString(60);
160: String classPath = "";
161: String[] command = { "java", selfDestruct, classPath,
162: getClass().getName(), "" + operations, "" + flags,
163: "" + value };
164: traceOperation("start: " + command);
165: Process p = Runtime.getRuntime().exec(command);
166: InputStream in = p.getInputStream();
167: OutputCatcher catcher = new OutputCatcher(in);
168: catcher.start();
169: String s = catcher.readLine(5 * 60 * 1000);
170: if (s == null) {
171: throw new IOException(
172: "No reply from process, command: " + command);
173: } else if (s.startsWith("READY")) {
174: traceOperation("got reply: " + s);
175: }
176: testWaitAfterAppStart();
177: p.destroy();
178: try {
179: traceOperation("backing up " + sequenceId);
180: Backup.execute(baseDir + "/haltSeq" + sequenceId
181: + ".zip", baseDir, null, true);
182: // new File(BASE_DIR + "/haltSeq" + (sequenceId-20) +
183: // ".zip").delete();
184: connect();
185: testCheckAfterCrash();
186: } catch (Exception e) {
187: File zip = new File(baseDir + "/haltSeq" + sequenceId
188: + ".zip");
189: File zipId = new File(baseDir + "/haltSeq" + sequenceId
190: + "-" + errorId + ".zip");
191: zip.renameTo(zipId);
192: printTime("ERROR: " + sequenceId + " " + errorId + " "
193: + e.toString());
194: e.printStackTrace();
195: errorId++;
196: } finally {
197: sequenceId++;
198: disconnect();
199: }
200: }
201: }
202:
203: protected void disconnect() {
204: try {
205: traceOperation("disconnect");
206: conn.close();
207: } catch (Exception e) {
208: trace("disconnect", e);
209: }
210: }
211:
212: public Connection getConnectionHSQLDB() throws Exception {
213: File lock = new File("test.lck");
214: while (lock.exists()) {
215: lock.delete();
216: System.gc();
217: }
218: Class.forName("org.hsqldb.jdbcDriver");
219: return DriverManager
220: .getConnection("jdbc:hsqldb:test", "sa", "");
221: }
222:
223: public Connection getConnectionDerby() throws Exception {
224: File lock = new File("test3/db.lck");
225: while (lock.exists()) {
226: lock.delete();
227: System.gc();
228: }
229: Class.forName("org.apache.derby.jdbc.EmbeddedDriver")
230: .newInstance();
231: try {
232: return DriverManager.getConnection(
233: "jdbc:derby:test3;create=true", "sa", "sa");
234: } catch (SQLException e) {
235: Exception e2 = e;
236: do {
237: e.printStackTrace();
238: e = e.getNextException();
239: } while (e != null);
240: throw e2;
241: }
242: }
243:
244: public void disconnectHSQLDB() {
245: try {
246: conn.createStatement().execute("SHUTDOWN");
247: } catch (Exception e) {
248: // ignore
249: }
250: // super.disconnect();
251: }
252:
253: public void disconnectDerby() {
254: // super.disconnect();
255: try {
256: Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
257: DriverManager.getConnection("jdbc:derby:;shutdown=true",
258: "sa", "sa");
259: } catch (Exception e) {
260: // ignore
261: }
262: }
263:
264: protected String getRandomString(int len) {
265: StringBuffer buff = new StringBuffer();
266: for (int i = 0; i < len; i++) {
267: buff.append('a' + random.nextInt(20));
268: }
269: return buff.toString();
270: }
271:
272: public TestBase init(TestAll conf) throws Exception {
273: super.init(conf);
274: baseDir = DIR;
275: return this;
276: }
277:
278: }
|