001/* 002 * #%L 003 * Netarchivesuite - harvester 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023 024package dk.netarkivet.harvester.datamodel; 025 026import static com.google.common.base.Preconditions.checkNotNull; 027 028import java.io.File; 029import java.io.FileInputStream; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.InputStreamReader; 033import java.io.LineNumberReader; 034import java.lang.reflect.Field; 035import java.sql.Connection; 036import java.sql.DriverManager; 037import java.sql.SQLException; 038import java.sql.Statement; 039 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043import dk.netarkivet.common.CommonSettings; 044import dk.netarkivet.common.exceptions.IOFailure; 045import dk.netarkivet.common.utils.FileUtils; 046import dk.netarkivet.common.utils.Settings; 047 048/** 049 * Utilities to allow testing databases. 050 * FIXME: Rename without Test as these are not specifically test related. 051 */ 052public class DatabaseTestUtils { 053 054 protected static final Logger log = LoggerFactory.getLogger(DatabaseTestUtils.class); 055 056 private static String dburi; 057 058 /** 059 * Get access to the database stored in the given file. This will start a new transaction that will be rolled back 060 * with dropDatabase. Only one connection can be taken at a time. 061 * 062 * @param resourcePath A file that contains a test database. 063 * @param dbCreationDir 064 * 065 */ 066 public static void createDatabase(String resourcePath, String dbname, File dbCreationDir) throws Exception { 067 Settings.set(CommonSettings.DB_MACHINE, ""); 068 Settings.set(CommonSettings.DB_PORT, ""); 069 Settings.set(CommonSettings.DB_DIR, ""); 070 071 FileUtils.removeRecursively(new File(dbCreationDir, dbname)); 072 073 final String dbfile = dbCreationDir + "/" + dbname; 074 075 // FIXME: change for h2 076 dburi = "jdbc:derby:" + dbfile; 077 078 long startTime = System.currentTimeMillis(); 079 080 try (Connection c = DriverManager.getConnection(dburi + ";create=true");){ 081 c.setAutoCommit(false); // Do not commit individual . 082 // locate create script first, next to resource 083 File createFile = new File(new File(resourcePath).getParentFile(), "create.sql"); 084 applyStatementsInInputStream(c, checkNotNull(new FileInputStream(createFile), "create.sql")); 085 086 // then populate it. 087 FileInputStream is = checkNotNull(new FileInputStream(resourcePath), resourcePath); 088 applyStatementsInInputStream(c, is); 089 090 c.commit(); 091 } 092 093 log.debug("Populated {} in {}(ms)", dbfile, (System.currentTimeMillis() - startTime)); 094 } 095 096 private static void applyStatementsInInputStream(Connection connection, InputStream is) throws SQLException, 097 IOException { 098 Statement statement = connection.createStatement(); 099 100 LineNumberReader br = new LineNumberReader(new InputStreamReader(is)); 101 String s = ""; 102 long count = 0; 103 try { 104 while ((s = br.readLine()) != null) { 105 log.debug(br.getLineNumber() + ": " + s); 106 if (s.trim().startsWith("#")) { 107 // skip comments 108 } else if (s.trim().length() == 0) { 109 // skip empty lines 110 } else { 111 count++; 112 { 113 // http://apache-database.10148.n7.nabble.com/Inserting-control-characters-in-SQL-td106944.html 114 s = s.replace("\\n", "\n"); 115 } 116 statement.execute(s); 117 } 118 } 119 } catch (SQLException e) { 120 throw new RuntimeException("Line " + br.getLineNumber() + ": " + s, e); 121 } 122 br.close(); 123 statement.close(); 124 if (count == 0) { 125 throw new RuntimeException("Executed " + count + " SQL commands."); 126 } 127 } 128 129 /** 130 * Get access to the database stored in the given file. This will start a new transaction that will be rolled back 131 * with dropDatabase. Only one connection can be taken at a time. 132 * 133 * @param resourcePath A file that contains a test database. 134 * @param dbCreationDir 135 */ 136 public static void createDatabase(String resourcePath, File dbCreationDir) throws Exception { 137 createDatabase(resourcePath, "derivenamefromresource", dbCreationDir); 138 } 139 140 /** 141 * Get a connection to the given sample harvest definition database and fool the HD DB connect class into thinking 142 * it should use that one. 143 * 144 * @param resourcePath Location of the sql files to create and populate the test DB. 145 * @param dbCreationDir 146 */ 147 public static void createHDDB(String resourcePath, String dbname, File dbCreationDir) throws Exception { 148 createDatabase(resourcePath, dbname, dbCreationDir); 149 } 150 151 /** 152 * Drop access to the database that's currently taken. 153 */ 154 public static void dropDatabase() throws Exception { 155 try { 156 final String shutdownUri = dburi + ";shutdown=true"; 157 DriverManager.getConnection(shutdownUri); 158 throw new IOFailure("Failed to shut down database"); 159 } catch (SQLException e) { 160 log.warn("Expected SQL-exception when shutting down database:", e); 161 } 162 // connectionPool.clear(); 163 // null field instance in DBSpecifics. 164 165 // inlined to break test dependency /tra 2014-05-19 166 // Field f = ReflectUtils.getPrivateField(DBSpecifics.class, 167 // "instance"); 168 Field f = DBSpecifics.class.getDeclaredField("instance"); 169 f.setAccessible(true); 170 171 f.set(null, null); 172 /* 173 * for (Thread t: connectionPool.keySet()) { final Connection connection = connectionPool.get(t); if 174 * (!(connection instanceof TestDBConnection)) { throw new UnknownID("Illegal connection " + connection); } try 175 * { if (savepoints.containsKey(t)) { connection.rollback(); // connection.rollback(savepoints.get(t)); 176 * savepoints.remove(t); } } catch (SQLException e) { System.out.println("Can't rollback: " + e); } 177 * connection.close(); } connectionPool.clear(); 178 */ 179 } 180 181 /** 182 * Drop the connection to the harvest definition database. 183 */ 184 public static void dropHDDB() throws Exception { 185 dropDatabase(); 186 log.debug("dropHDDB() 1"); 187 HarvestDBConnection.cleanup(); 188 } 189}