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.poweroff;
007:
008: import java.io.File;
009: import java.io.FileInputStream;
010: import java.io.FileOutputStream;
011: import java.io.IOException;
012: import java.io.InputStream;
013: import java.io.OutputStream;
014: import java.io.PrintWriter;
015: import java.util.Date;
016: import java.security.SecureRandom;
017: import java.sql.Connection;
018: import java.sql.Driver;
019: import java.sql.DriverManager;
020: import java.sql.PreparedStatement;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.text.SimpleDateFormat;
025: import java.util.ArrayList;
026: import java.util.List;
027: import java.util.Random;
028: import java.util.zip.ZipEntry;
029: import java.util.zip.ZipOutputStream;
030:
031: import org.h2.util.IOUtils;
032:
033: /**
034: * This standalone test checks if recovery of a database works after power
035: * failure.
036: */
037: public class TestRecover {
038:
039: private Random random;
040: private static final String NODE = System.getProperty("test.node",
041: "");
042: private static final String DIR = System.getProperty("test.dir",
043: "/temp/db");
044:
045: // private static final String DIR =
046: // System.getProperty("test.dir", "/temp/derby");
047: // private static final String URL =
048: // System.getProperty("test.url",
049: // "jdbc:derby:/temp/derby/data/test;create=true");
050: // private static final String DRIVER =
051: // System.getProperty("test.driver",
052: // "org.apache.derby.jdbc.EmbeddedDriver");
053:
054: private static final String TEST_DIRECTORY = DIR + "/data" + NODE;
055: private static final String BACKUP_DIRECTORY = DIR + "/last";
056: private static final String URL = System.getProperty("test.url",
057: "jdbc:h2:" + TEST_DIRECTORY + "/test;MAX_LOG_SIZE=2");
058: private static final String DRIVER = System.getProperty(
059: "test.driver", "org.h2.Driver");
060:
061: public static void main(String[] args) throws Exception {
062: new TestRecover().runTest(args);
063: }
064:
065: private void runTest(String[] args) throws Exception {
066: System.out.println("backup...");
067: new File(TEST_DIRECTORY).mkdirs();
068: File backup = backup(TEST_DIRECTORY, BACKUP_DIRECTORY, "data",
069: 10, NODE);
070: System.out.println("check consistency...");
071: if (!testConsistency()) {
072: System.out.println("error! renaming file");
073: backup.renameTo(new File(backup.getParentFile(), "error-"
074: + backup.getName()));
075: }
076: System.out.println("deleting old run...");
077: deleteRecursive(new File(TEST_DIRECTORY));
078: System.out.println("testing...");
079: testLoop();
080: }
081:
082: static File backup(String sourcePath, String targetPath,
083: String basePath, int max, String node) throws Exception {
084: File root = new File(targetPath);
085: if (!root.exists()) {
086: root.mkdirs();
087: }
088: while (true) {
089: File[] list = root.listFiles();
090: File oldest = null;
091: int count = 0;
092: for (int i = 0; i < list.length; i++) {
093: File f = list[i];
094: String name = f.getName();
095: if (f.isFile() && name.startsWith("backup")
096: && name.endsWith(".zip")) {
097: count++;
098: if (oldest == null
099: || f.lastModified() < oldest.lastModified()) {
100: oldest = f;
101: }
102: }
103: }
104: if (count < max) {
105: break;
106: }
107: oldest.delete();
108: }
109: SimpleDateFormat sd = new SimpleDateFormat("yyMMdd-HHmmss");
110: String date = sd.format(new Date());
111: File zipFile = new File(root, "backup-" + date + "-" + node
112: + ".zip");
113: ArrayList list = new ArrayList();
114: File base = new File(sourcePath);
115: listRecursive(list, base);
116: if (list.size() == 0) {
117: FileOutputStream out = new FileOutputStream(zipFile);
118: out.close();
119: } else {
120: OutputStream out = null;
121: try {
122: out = new FileOutputStream(zipFile);
123: ZipOutputStream zipOut = new ZipOutputStream(out);
124: String baseName = base.getAbsolutePath();
125: for (int i = 0; i < list.size(); i++) {
126: File f = (File) list.get(i);
127: String fileName = f.getAbsolutePath();
128: String entryName = fileName;
129: if (fileName.startsWith(baseName)) {
130: entryName = entryName.substring(baseName
131: .length());
132: }
133: if (entryName.startsWith("\\")) {
134: entryName = entryName.substring(1);
135: }
136: if (!entryName.startsWith("/")) {
137: entryName = "/" + entryName;
138: }
139: ZipEntry entry = new ZipEntry(basePath + entryName);
140: zipOut.putNextEntry(entry);
141: InputStream in = null;
142: try {
143: in = new FileInputStream(fileName);
144: IOUtils.copyAndCloseInput(in, zipOut);
145: } finally {
146: IOUtils.closeSilently(in);
147: }
148: zipOut.closeEntry();
149: }
150: zipOut.closeEntry();
151: zipOut.close();
152: } finally {
153: IOUtils.closeSilently(out);
154: }
155: }
156: return zipFile;
157: }
158:
159: static void listRecursive(List list, File file) throws IOException {
160: File[] l = file.listFiles();
161: for (int i = 0; l != null && i < l.length; i++) {
162: File f = l[i];
163: if (f.isDirectory()) {
164: listRecursive(list, f);
165: } else {
166: list.add(f);
167: }
168: }
169: }
170:
171: static void deleteRecursive(File file) throws IOException {
172: if (file.isDirectory()) {
173: File[] list = file.listFiles();
174: for (int i = 0; i < list.length; i++) {
175: deleteRecursive(list[i]);
176: }
177: }
178: if (file.exists() && !file.delete()) {
179: throw new IOException("Could not delete "
180: + file.getAbsolutePath());
181: }
182: }
183:
184: private void testLoop() throws Exception {
185: random = new SecureRandom();
186: while (true) {
187: runOneTest(random.nextInt());
188: }
189: }
190:
191: Connection openConnection() throws Exception {
192: Class.forName(DRIVER);
193: Connection conn = DriverManager.getConnection(URL, "sa", "sa");
194: Statement stat = conn.createStatement();
195: try {
196: stat
197: .execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))");
198: } catch (SQLException e) {
199: // ignore
200: }
201: return conn;
202: }
203:
204: private void closeConnection(Connection conn) {
205: try {
206: conn.close();
207: } catch (SQLException e) {
208: // ignore
209: }
210: if (DRIVER.startsWith("org.apache.derby")) {
211: try {
212: DriverManager
213: .getConnection("jdbc:derby:;shutdown=true");
214: } catch (SQLException e) {
215: // ignore
216: }
217: try {
218: Driver driver = (Driver) Class.forName(DRIVER)
219: .newInstance();
220: DriverManager.registerDriver(driver);
221: } catch (Exception e) {
222: e.printStackTrace();
223: }
224: }
225: }
226:
227: private void runOneTest(int i) throws Exception {
228: Random random = new Random(i);
229: Connection conn = openConnection();
230: PreparedStatement prepInsert = null;
231: PreparedStatement prepDelete = null;
232: conn.setAutoCommit(false);
233: for (int id = 0;; id++) {
234: boolean rollback = random.nextInt(10) == 1;
235: int len;
236: if (random.nextInt(10) == 1) {
237: len = random.nextInt(100) * 2;
238: } else {
239: len = random.nextInt(2) * 2;
240: }
241: if (rollback && random.nextBoolean()) {
242: // make the length odd
243: len++;
244: }
245: // byte[] data = new byte[len];
246: // random.nextBytes(data);
247: int op = random.nextInt();
248: if (op % 1000000 == 0) {
249: closeConnection(conn);
250: conn = openConnection();
251: conn.setAutoCommit(false);
252: prepInsert = null;
253: prepDelete = null;
254: }
255: if (random.nextBoolean()) {
256: if (prepInsert == null) {
257: prepInsert = conn
258: .prepareStatement("INSERT INTO TEST(ID, NAME) VALUES(?, ?)");
259: }
260: prepInsert.setInt(1, id);
261: prepInsert.setString(2, "" + len);
262: prepInsert.execute();
263: } else {
264: ResultSet rs = conn.createStatement().executeQuery(
265: "SELECT COUNT(*) FROM TEST");
266: rs.next();
267: int count = rs.getInt(1);
268: rs.close();
269: if (count > 1000) {
270: if (prepDelete == null) {
271: prepDelete = conn
272: .prepareStatement("DELETE FROM TEST WHERE ROWNUM <= 4");
273: }
274: prepDelete.execute();
275: }
276: }
277: if (rollback) {
278: conn.rollback();
279: } else {
280: conn.commit();
281: }
282: }
283: }
284:
285: private boolean testConsistency() {
286: FileOutputStream out = null;
287: PrintWriter p = null;
288: try {
289: out = new FileOutputStream(TEST_DIRECTORY + "/result.txt");
290: p = new PrintWriter(out);
291: p.println("Results");
292: p.flush();
293: } catch (Throwable t) {
294: t.printStackTrace();
295: System.exit(0);
296: }
297: Connection conn = null;
298: try {
299: conn = openConnection();
300: ResultSet rs = conn.createStatement().executeQuery(
301: "SELECT * FROM TEST");
302: int max = 0;
303: int count = 0;
304: while (rs.next()) {
305: count++;
306: int id = rs.getInt("ID");
307: String name = rs.getString("NAME");
308: int value = Integer.parseInt(name);
309: if (value % 2 == 1) {
310: throw new Exception("unexpected odd entry " + id);
311: }
312: max = Math.max(max, id);
313: }
314: rs.close();
315: closeConnection(conn);
316: System.out
317: .println("max row id: " + max + " rows: " + count);
318: return true;
319: } catch (Throwable t) {
320: t.printStackTrace();
321: t.printStackTrace(p);
322: return false;
323: } finally {
324: if (conn != null) {
325: try {
326: closeConnection(conn);
327: } catch (Throwable t2) {
328: t2.printStackTrace();
329: t2.printStackTrace(p);
330: }
331: }
332: p.flush();
333: p.close();
334: IOUtils.closeSilently(out);
335: }
336: }
337:
338: }
|