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. //FIXME: Rename without Test as these are not specifically test related. 050 */ 051public class DatabaseTestUtils { 052 053 protected static final Logger log = LoggerFactory.getLogger(DatabaseTestUtils.class); 054 055 private static String dburi; 056 057 /** 058 * Get access to the database stored in the given file. This will start a new transaction that will be rolled back 059 * with dropDatabase. Only one connection can be taken at a time. 060 * 061 * @param resourcePath A file that contains a test database. 062 * @param dbCreationDir 063 * @return a connection to the database stored in the given file 064 */ 065 public static void createDatabase(String resourcePath, String dbname, File dbCreationDir) throws Exception { 066 Settings.set(CommonSettings.DB_MACHINE, ""); 067 Settings.set(CommonSettings.DB_PORT, ""); 068 Settings.set(CommonSettings.DB_DIR, ""); 069 070 FileUtils.removeRecursively(new File(dbCreationDir, dbname)); 071 072 final String dbfile = dbCreationDir + "/" + dbname; 073 074 // FIXME: change for h2 075 dburi = "jdbc:derby:" + dbfile; 076 077 long startTime = System.currentTimeMillis(); 078 079 try (Connection c = DriverManager.getConnection(dburi + ";create=true");){ 080 c.setAutoCommit(false); // Do not commit individual . 081 // locate create script first, next to resource 082 File createFile = new File(new File(resourcePath).getParentFile(), "create.sql"); 083 applyStatementsInInputStream(c, checkNotNull(new FileInputStream(createFile), "create.sql")); 084 085 // then populate it. 086 FileInputStream is = checkNotNull(new FileInputStream(resourcePath), resourcePath); 087 applyStatementsInInputStream(c, is); 088 089 c.commit(); 090 } 091 092 log.debug("Populated {} in {}(ms)", dbfile, (System.currentTimeMillis() - startTime)); 093 } 094 095 private static void applyStatementsInInputStream(Connection connection, InputStream is) throws SQLException, 096 IOException { 097 Statement statement = connection.createStatement(); 098 099 LineNumberReader br = new LineNumberReader(new InputStreamReader(is)); 100 String s = ""; 101 long count = 0; 102 try { 103 while ((s = br.readLine()) != null) { 104 log.debug(br.getLineNumber() + ": " + s); 105 if (s.trim().startsWith("#")) { 106 // skip comments 107 } else if (s.trim().length() == 0) { 108 // skip empty lines 109 } else { 110 count++; 111 { 112 // http://apache-database.10148.n7.nabble.com/Inserting-control-characters-in-SQL-td106944.html 113 s = s.replace("\\n", "\n"); 114 } 115 statement.execute(s); 116 } 117 } 118 } catch (SQLException e) { 119 throw new RuntimeException("Line " + br.getLineNumber() + ": " + s, e); 120 } 121 br.close(); 122 statement.close(); 123 if (count == 0) { 124 throw new RuntimeException("Executed " + count + " SQL commands."); 125 } 126 } 127 128 /** 129 * Get access to the database stored in the given file. This will start a new transaction that will be rolled back 130 * with dropDatabase. Only one connection can be taken at a time. 131 * 132 * @param resourcePath A file that contains a test database. 133 * @param dbCreationDir 134 * @return a connection to the database stored in the given file 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 * @return a connection to the given sample harvest definition database 147 */ 148 public static void createHDDB(String resourcePath, String dbname, File dbCreationDir) throws Exception { 149 createDatabase(resourcePath, dbname, dbCreationDir); 150 } 151 152 /** 153 * Drop access to the database that's currently taken. 154 */ 155 public static void dropDatabase() throws Exception { 156 try { 157 final String shutdownUri = dburi + ";shutdown=true"; 158 DriverManager.getConnection(shutdownUri); 159 throw new IOFailure("Failed to shut down database"); 160 } catch (SQLException e) { 161 log.warn("Expected SQL-exception when shutting down database:", e); 162 } 163 // connectionPool.clear(); 164 // null field instance in DBSpecifics. 165 166 // inlined to break test dependency /tra 2014-05-19 167 // Field f = ReflectUtils.getPrivateField(DBSpecifics.class, 168 // "instance"); 169 Field f = DBSpecifics.class.getDeclaredField("instance"); 170 f.setAccessible(true); 171 172 f.set(null, null); 173 /* 174 * for (Thread t: connectionPool.keySet()) { final Connection connection = connectionPool.get(t); if 175 * (!(connection instanceof TestDBConnection)) { throw new UnknownID("Illegal connection " + connection); } try 176 * { if (savepoints.containsKey(t)) { connection.rollback(); // connection.rollback(savepoints.get(t)); 177 * savepoints.remove(t); } } catch (SQLException e) { System.out.println("Can't rollback: " + e); } 178 * connection.close(); } connectionPool.clear(); 179 */ 180 } 181 182 /** 183 * Drop the connection to the harvest definition database. 184 */ 185 public static void dropHDDB() throws Exception { 186 dropDatabase(); 187 log.debug("dropHDDB() 1"); 188 HarvestDBConnection.cleanup(); 189 } 190}