1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.test;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.commons.lang.time.DurationFormatUtils;
20 import org.apache.commons.lang.time.StopWatch;
21 import org.apache.log4j.Logger;
22 import org.junit.Assert;
23 import org.kuali.rice.core.api.config.property.ConfigContext;
24 import org.kuali.rice.core.api.lifecycle.BaseLifecycle;
25 import org.springframework.dao.DataAccessException;
26 import org.springframework.jdbc.core.ConnectionCallback;
27 import org.springframework.jdbc.core.JdbcTemplate;
28 import org.springframework.jdbc.core.StatementCallback;
29 import org.springframework.transaction.PlatformTransactionManager;
30 import org.springframework.transaction.TransactionStatus;
31 import org.springframework.transaction.support.TransactionCallback;
32 import org.springframework.transaction.support.TransactionTemplate;
33
34 import javax.sql.DataSource;
35 import java.sql.Connection;
36 import java.sql.DatabaseMetaData;
37 import java.sql.ResultSet;
38 import java.sql.SQLException;
39 import java.sql.Statement;
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44
45
46
47
48
49
50
51
52
53
54
55 public class ClearDatabaseLifecycle extends BaseLifecycle {
56
57 protected static final Logger LOG = Logger.getLogger(ClearDatabaseLifecycle.class);
58
59 private List<String> tablesToClear = new ArrayList<String>();
60 private List<String> tablesNotToClear = new ArrayList<String>();
61
62 public ClearDatabaseLifecycle() {
63 addStandardTables();
64 }
65
66 public ClearDatabaseLifecycle(List<String> tablesToClear, List<String> tablesNotToClear) {
67 this.tablesToClear = tablesToClear;
68 this.tablesNotToClear = tablesNotToClear;
69 addStandardTables();
70 }
71
72 protected void addStandardTables() {
73 tablesNotToClear.add("BIN.*");
74 tablesNotToClear.add(".*_S");
75 }
76
77 public static final String TEST_TABLE_NAME = "EN_UNITTEST_T";
78
79 public void start() throws Exception {
80 String useClearDatabaseLifecycle = ConfigContext.getCurrentContextConfig().getProperty("use.clearDatabaseLifecycle");
81
82 if (useClearDatabaseLifecycle != null && !Boolean.valueOf(useClearDatabaseLifecycle)) {
83 LOG.debug("Skipping ClearDatabaseLifecycle due to property: use.clearDatabaseLifecycle=" + useClearDatabaseLifecycle);
84 return;
85 }
86
87 final DataSource dataSource = TestHarnessServiceLocator.getDataSource();
88 clearTables(TestHarnessServiceLocator.getJtaTransactionManager(), dataSource);
89 super.start();
90 }
91
92 protected Boolean isTestTableInSchema(final Connection connection) throws SQLException {
93 Assert.assertNotNull("Connection could not be located.", connection);
94 ResultSet resultSet = null;
95 try {
96 resultSet = connection.getMetaData().getTables(null, connection.getMetaData().getUserName().toUpperCase(), TEST_TABLE_NAME, null);
97 return new Boolean(resultSet.next());
98 } finally {
99 if (resultSet != null) {
100 resultSet.close();
101 }
102 }
103 }
104
105 protected void verifyTestEnvironment(final DataSource dataSource) {
106 new JdbcTemplate(dataSource).execute(new ConnectionCallback<Object>() {
107 public Object doInConnection(final Connection connection) throws SQLException {
108 String dbUrl = connection.getMetaData().getURL();
109 Assert.assertTrue("No table named '" + TEST_TABLE_NAME + "' was found in the configured database. " +
110 dbUrl + " You are attempting to run tests against a non-test database!!!", isTestTableInSchema(connection));
111 return null;
112 }
113 });
114 }
115
116 protected void clearTables(final PlatformTransactionManager transactionManager, final DataSource dataSource) {
117 Assert.assertNotNull("DataSource could not be located.", dataSource);
118 try {
119 StopWatch s = new StopWatch();
120 s.start();
121 new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
122 public Object doInTransaction(final TransactionStatus status) {
123 verifyTestEnvironment(dataSource);
124 return new JdbcTemplate(dataSource).execute(new StatementCallback() {
125 public Object doInStatement(Statement statement) throws SQLException {
126 String schemaName = statement.getConnection().getMetaData().getUserName().toUpperCase();
127 LOG.info("Clearing tables for schema " + schemaName);
128 if (StringUtils.isBlank(schemaName)) {
129 Assert.fail("Empty schema name given");
130 }
131 final List<String> reEnableConstraints = new ArrayList<String>();
132 DatabaseMetaData metaData = statement.getConnection().getMetaData();
133 Map<String, List<String[]>> exportedKeys = indexExportedKeys(metaData, schemaName);
134 final ResultSet resultSet = metaData.getTables(null, schemaName, null, new String[] { "TABLE" });
135 final StringBuilder logStatements = new StringBuilder();
136 while (resultSet.next()) {
137 String tableName = resultSet.getString("TABLE_NAME");
138 if (shouldTableBeCleared(tableName)) {
139 if (!isUsingDerby(metaData) && isUsingOracle(metaData)) {
140 List<String[]> exportedKeyNames = exportedKeys.get(tableName);
141 if (exportedKeyNames != null) {
142 for (String[] exportedKeyName : exportedKeyNames) {
143 final String fkName = exportedKeyName[0];
144 final String fkTableName = exportedKeyName[1];
145 final String disableConstraint = "ALTER TABLE " + fkTableName + " DISABLE CONSTRAINT " + fkName;
146 logStatements.append("Disabling constraints using statement ->" + disableConstraint + "<-\n");
147 statement.addBatch(disableConstraint);
148 reEnableConstraints.add("ALTER TABLE " + fkTableName + " ENABLE CONSTRAINT " + fkName);
149 }
150 }
151 } else if (isUsingMySQL(metaData)) {
152 statement.addBatch("SET FOREIGN_KEY_CHECKS = 0");
153 }
154 String deleteStatement = "DELETE FROM " + tableName;
155 logStatements.append("Clearing contents using statement ->" + deleteStatement + "<-\n");
156 statement.addBatch(deleteStatement);
157 }
158 }
159 for (final String constraint : reEnableConstraints) {
160 logStatements.append("Enabling constraints using statement ->" + constraint + "<-\n");
161 statement.addBatch(constraint);
162 }
163 if (isUsingMySQL(metaData)) {
164 statement.addBatch("SET FOREIGN_KEY_CHECKS = 1");
165 }
166 LOG.info(logStatements);
167
168 int[] results = statement.executeBatch();
169 for (int index = 0; index < results.length; index++) {
170 if (results[index] == Statement.EXECUTE_FAILED) {
171 Assert.fail("Execution of database clear statement failed.");
172 }
173
174 }
175 resultSet.close();
176 LOG.info("Tables successfully cleared for schema " + schemaName);
177 return null;
178 }
179 });
180 }
181 });
182 s.stop();
183 LOG.info("Time to clear tables: " + DurationFormatUtils.formatDurationHMS(s.getTime()));
184 } catch (Exception e) {
185 LOG.error(e);
186 throw new RuntimeException(e);
187 }
188 }
189
190 protected Map<String, List<String[]>> indexExportedKeys(DatabaseMetaData metaData, String schemaName) throws SQLException {
191 Map<String, List<String[]>> exportedKeys = new HashMap<String, List<String[]>>();
192 if (!isUsingDerby(metaData) && isUsingOracle(metaData)) {
193 ResultSet keyResultSet = metaData.getExportedKeys(null, schemaName, null);
194 while (keyResultSet.next()) {
195 String tableName = keyResultSet.getString("PKTABLE_NAME");
196 if (shouldTableBeCleared(tableName)) {
197 List<String[]> exportedKeyNames = exportedKeys.get(tableName);
198 if (exportedKeyNames == null) {
199 exportedKeyNames = new ArrayList<String[]>();
200 exportedKeys.put(tableName, exportedKeyNames);
201 }
202 final String fkName = keyResultSet.getString("FK_NAME");
203 final String fkTableName = keyResultSet.getString("FKTABLE_NAME");
204 exportedKeyNames.add(new String[] { fkName, fkTableName });
205 }
206 }
207 keyResultSet.close();
208 }
209 return exportedKeys;
210 }
211
212 private boolean shouldTableBeCleared(String tableName) {
213 if (getTablesNotToClear() != null && !getTablesNotToClear().isEmpty()) {
214 for (String tableNotToClear : getTablesNotToClear()) {
215 if (tableName.toUpperCase().matches(tableNotToClear.toUpperCase())) {
216 return false;
217 }
218 }
219 }
220 if (getTablesToClear() != null && !getTablesToClear().isEmpty()) {
221 for (String tableToClear : getTablesToClear()) {
222 if (tableName.toUpperCase().matches(tableToClear.toUpperCase())) {
223 return true;
224 }
225 }
226 return false;
227 }
228
229 return true;
230 }
231
232 private boolean isUsingDerby(DatabaseMetaData metaData) throws SQLException {
233 return metaData.getDriverName().toLowerCase().contains("derby");
234 }
235
236 private boolean isUsingOracle(DatabaseMetaData metaData) throws SQLException {
237 return metaData.getDriverName().toLowerCase().contains("oracle");
238 }
239
240 private boolean isUsingMySQL(DatabaseMetaData metaData) throws SQLException {
241 return metaData.getDriverName().toLowerCase().contains("mysql");
242 }
243
244
245 public List<String> getTablesToClear() {
246 return this.tablesToClear;
247 }
248
249 public List<String> getTablesNotToClear() {
250 return this.tablesNotToClear;
251 }
252 }