1 package fr.ifremer.tutti.persistence.test;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import com.google.common.base.Charsets;
26 import com.google.common.base.Preconditions;
27 import com.google.common.base.Predicate;
28 import com.google.common.collect.Lists;
29 import com.google.common.collect.Sets;
30 import com.google.common.io.Files;
31 import fr.ifremer.tutti.TuttiConfiguration;
32 import fr.ifremer.tutti.TuttiConfigurationOption;
33 import fr.ifremer.tutti.persistence.service.TuttiPersistenceServiceLocator;
34 import fr.ifremer.tutti.util.Jdbcs;
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.io.IOUtils;
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.junit.Assume;
41 import org.junit.rules.TestRule;
42 import org.junit.runner.Description;
43 import org.junit.runners.model.Statement;
44 import org.nuiton.config.ApplicationConfig;
45 import org.nuiton.converter.ConverterUtil;
46 import org.nuiton.util.FileUtil;
47
48 import java.io.BufferedReader;
49 import java.io.BufferedWriter;
50 import java.io.File;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.io.OutputStream;
54 import java.util.List;
55 import java.util.Properties;
56 import java.util.Set;
57
58
59
60
61
62
63
64 public class DatabaseResource implements TestRule {
65
66
67 private static final Log log = LogFactory.getLog(DatabaseResource.class);
68
69 public static long BUILD_TIMESTAMP = System.nanoTime();
70
71 private File resourceDirectory;
72
73 private TuttiConfiguration config;
74
75 private DatabaseFixtures fixtures;
76
77 protected final String beanFactoryReferenceLocation;
78
79 private final String beanRefFactoryReferenceId;
80
81 private final boolean writeDb;
82
83 private String dbName;
84
85 private boolean destroyResources;
86
87 public static DatabaseResource readDb() {
88 return new DatabaseResource("");
89 }
90
91 public static DatabaseResource writeDb() {
92 return new DatabaseResource("", true);
93 }
94
95 public static DatabaseResource readDb(String dbName) {
96 return new DatabaseResource(dbName);
97 }
98
99 public static DatabaseResource writeDb(String dbName) {
100 return new DatabaseResource(dbName, true);
101 }
102
103 public static DatabaseResource noDb() {
104 return new DatabaseResource(
105 "", "beanRefFactoryWitNoDb.xml", "TuttiBeanRefFactoryWithNoDb");
106 }
107
108 protected DatabaseResource(String dbName) {
109 this(dbName, null, null, false);
110 }
111
112 protected DatabaseResource(String dbName, boolean writeDb) {
113 this(dbName, null, null, writeDb);
114 }
115
116 protected DatabaseResource(String dbName, String beanFactoryReferenceLocation,
117 String beanRefFactoryReferenceId) {
118 this(dbName, beanFactoryReferenceLocation,
119 beanRefFactoryReferenceId, false);
120 }
121
122 protected DatabaseResource(String dbName, String beanFactoryReferenceLocation,
123 String beanRefFactoryReferenceId,
124 boolean writeDb) {
125 this.dbName = dbName;
126 this.beanFactoryReferenceLocation = beanFactoryReferenceLocation;
127 this.beanRefFactoryReferenceId = beanRefFactoryReferenceId;
128 this.writeDb = writeDb;
129 this.destroyResources = true;
130 }
131
132 public boolean withDb() {
133 return dbName!=null;
134 }
135
136
137 public TuttiConfiguration getConfig() {
138 return config;
139 }
140
141 public DatabaseFixtures getFixtures() {
142 return fixtures;
143 }
144
145 public File getResourceDirectory(String name) {
146 return new File(resourceDirectory, name);
147 }
148
149 public void setDestroyResources(boolean destroyResources) {
150 this.destroyResources = destroyResources;
151 }
152
153 public String getDbName() {
154 return dbName;
155 }
156
157 public boolean isWriteDb() {
158 return writeDb;
159 }
160
161 @Override
162 public Statement apply(final Statement base, final Description description) {
163
164 return new Statement() {
165 @Override
166 public void evaluate() throws Throwable {
167 before(description);
168 try {
169 base.evaluate();
170 } finally {
171 after(description);
172 }
173 }
174 };
175 }
176
177 Class<?> testClass;
178
179 public void prepareConfig(ApplicationConfig applicationConfig,
180 File resourceDirectory) {
181
182 TuttiConfiguration.getDefaultApplicationConfig(applicationConfig);
183
184 applicationConfig.setDefaultOption(TuttiConfigurationOption.DATA_DIRECTORY.getKey(),
185 new File(resourceDirectory, "data").getAbsolutePath());
186
187 if (!writeDb) {
188
189
190 File dbDirectory = FileUtil.getFileFromPaths(new File("src"), "test", "data", dbName);
191
192 applicationConfig.setDefaultOption(TuttiConfigurationOption.DB_DIRECTORY.getKey(),
193 dbDirectory.getAbsolutePath());
194
195
196 applicationConfig.setDefaultOption(TuttiConfigurationOption.JDBC_URL.getKey(),
197 String.format("jdbc:hsqldb:file:%s/allegro", dbDirectory.getAbsolutePath()));
198
199 }
200 }
201
202 public File copyClassPathResource(String path, String destinationName) throws IOException {
203 InputStream inputStream = getClass().getResourceAsStream("/" + path);
204 Preconditions.checkNotNull(inputStream, "Could not find " + path + " in test class-path");
205 File output = new File(resourceDirectory, destinationName);
206
207 OutputStream outputStream = FileUtils.openOutputStream(output);
208 try {
209 IOUtils.copy(inputStream, outputStream);
210 inputStream.close();
211 outputStream.close();
212 } finally {
213 IOUtils.closeQuietly(inputStream);
214 IOUtils.closeQuietly(outputStream);
215 }
216 return output;
217 }
218
219 protected void before(Description description) throws Throwable {
220
221 TuttiTestSupport.registerDescriptionAndResource(description, this);
222
223 testClass = description.getTestClass();
224
225 boolean withDb = withDb();
226
227 boolean defaultDbName = withDb && StringUtils.isEmpty(dbName);
228
229 if (defaultDbName) {
230 dbName = "db";
231 }
232
233 if (log.isInfoEnabled()) {
234 log.info("Prepare test " + testClass);
235 }
236
237 fixtures = new DatabaseFixtures();
238
239 resourceDirectory = FileUtil.getTestSpecificDirectory(testClass, "", description.getMethodName(), BUILD_TIMESTAMP);
240 addToDestroy(resourceDirectory);
241
242 ConverterUtil.deregister();
243 ConverterUtil.initConverters();
244
245 ApplicationConfig applicationConfig= createApplicationConfig(defaultDbName);
246
247 prepareConfig(applicationConfig, resourceDirectory);
248
249 applicationConfig.parse();
250
251 config = new TuttiConfiguration(applicationConfig);
252 TuttiConfiguration.setInstance(config);
253
254 if (withDb) {
255 prepareDb();
256 }
257
258 config.initConfig();
259
260 if (withDb) {
261 if (log.isInfoEnabled()) {
262 log.info("Use db: " + config.getJdbcUrl());
263 }
264 } else {
265 if (log.isInfoEnabled()) {
266 log.info("No db configured.");
267 }
268 }
269
270 if (beanFactoryReferenceLocation != null) {
271 TuttiPersistenceServiceLocator.initTutti(
272 beanFactoryReferenceLocation,
273 beanRefFactoryReferenceId);
274 }
275 }
276
277 protected ApplicationConfig createApplicationConfig(boolean defaultDbName) {
278
279 ApplicationConfig applicationConfig=null;
280 if (withDb()) {
281
282 String configFilename = writeDb ?
283 "tutti-test-write" :
284 "tutti-test-read";
285 if (!defaultDbName) {
286 configFilename += "-" + dbName;
287 }
288
289 configFilename += ".properties";
290 InputStream resourceAsStream = getClass().getResourceAsStream("/" + configFilename);
291
292 if (resourceAsStream!=null) {
293
294 if (log.isInfoEnabled()) {
295 log.info("Use configuration file found in classpath at " + configFilename);
296 }
297
298 IOUtils.closeQuietly(resourceAsStream);
299
300 applicationConfig = new ApplicationConfig(configFilename);
301
302 }
303
304 }
305
306 if (applicationConfig==null) {
307
308 if (log.isInfoEnabled()) {
309 log.info("Use default configuration, with no configuration from class-path");
310 }
311 applicationConfig = new ApplicationConfig();
312 }
313
314 return applicationConfig;
315
316 }
317
318 protected void prepareDb() throws IOException {
319
320 File db;
321
322 if (writeDb) {
323
324
325 db = FileUtil.getFileFromPaths(new File("src"), "test", "data", dbName);
326
327 } else {
328
329
330 db = config.getDbDirectory();
331 }
332
333 if (!db.exists()) {
334
335 if (log.isWarnEnabled()) {
336 log.warn("Could not find db at " + db + ", test [" + testClass + "] is skipped.");
337 }
338 Assume.assumeTrue(false);
339 }
340
341 if (writeDb) {
342 copyDb(config.getDbDirectory(), false, null);
343 }
344
345 addToDestroy(config.getDbAttachmentDirectory());
346
347
348 File dbConfig = new File(config.getDbDirectory(), config.getDbName() + ".properties");
349 Properties p = new Properties();
350 BufferedReader reader = Files.newReader(dbConfig, Charsets.UTF_8);
351 p.load(reader);
352 reader.close();
353
354 if (log.isDebugEnabled()) {
355 log.debug("Db config: " + dbConfig + "\n" + p);
356 }
357
358 if (writeDb) {
359
360
361 String readonly = p.getProperty("readonly");
362 Preconditions.checkNotNull(readonly, "Could not find readonly property on db confg: " + dbConfig);
363 Preconditions.checkState("false".equals(readonly), "readonly property must be at false value in write mode test in db confg: " + dbConfig);
364 } else {
365
366 String readonly = p.getProperty("readonly");
367 Preconditions.checkNotNull(readonly, "Could not find readonly property on db confg: " + dbConfig);
368 Preconditions.checkState("true".equals(readonly), "readonly property must be at true value in read mode test in db confg: " + dbConfig);
369 }
370
371 }
372
373 protected final Set<File> toDetroy = Sets.newHashSet();
374
375 public void addToDestroy(File dir) {
376 toDetroy.add(dir);
377 }
378
379 public void copyDb(String dbDirectory, boolean readonly, Properties p) throws IOException {
380 File externalDbFile = getResourceDirectory(dbDirectory);
381 copyDb(externalDbFile, readonly, p);
382 }
383
384 public void copyDb(File target, boolean readonly, Properties p) throws IOException {
385 File db = FileUtil.getFileFromPaths(new File("src"), "test", "data", dbName);
386 if (!db.exists()) {
387
388 if (log.isWarnEnabled()) {
389 log.warn("Could not find db at " + db + ", test [" + testClass + "] is skipped.");
390 }
391 Assume.assumeTrue(false);
392 }
393 addToDestroy(target);
394 FileUtils.copyDirectory(db, target);
395 if (p != null) {
396 Jdbcs.fillConnectionProperties(
397 p,
398 Jdbcs.getJdbcUrl(target, config.getDbName()),
399 config.getJdbcUsername(),
400 config.getJdbcPassword());
401 }
402
403
404 File dbConfig = new File(target, config.getDbName() + ".properties");
405 Properties dbconf = new Properties();
406 BufferedReader reader = Files.newReader(dbConfig, Charsets.UTF_8);
407 dbconf.load(reader);
408 reader.close();
409
410
411 dbconf.setProperty("readonly", String.valueOf(readonly));
412 BufferedWriter writer = Files.newWriter(dbConfig, Charsets.UTF_8);
413 dbconf.store(writer, "");
414 writer.close();
415
416 }
417
418 public void cleanResources(Description description) {
419
420 if (log.isDebugEnabled()) {
421 log.debug("Clean resources for test: " + description);
422 }
423
424 if (destroyResources) {
425
426
427 for (File file : toDetroy) {
428 if (file.exists()) {
429 if (log.isInfoEnabled()) {
430 log.info("Destroy directory: " + file);
431 }
432 try {
433 FileUtils.deleteDirectory(file);
434 } catch (IOException e) {
435 if (log.isErrorEnabled()) {
436 log.error("Could not delete directory: " + file, e);
437 }
438 }
439 }
440 }
441 } else {
442
443 if (log.isWarnEnabled()) {
444 log.warn("Won't destroy directories (destroyResources flag was false).");
445 }
446
447 }
448
449 toDetroy.clear();
450
451 }
452
453 protected void after(Description description) {
454 if (log.isInfoEnabled()) {
455 log.info("After test " + description);
456 }
457
458 closeSpring();
459 TuttiConfiguration.setInstance(null);
460
461 if (description.getMethodName() == null) {
462
463
464 CleanResourcesRule.cleanResources(description);
465
466 }
467
468 }
469
470 protected void closeSpring() {
471
472 TuttiPersistenceServiceLocator.shutdownTutti();
473
474 if (beanFactoryReferenceLocation != null) {
475
476
477 TuttiPersistenceServiceLocator.initTuttiDefault();
478 }
479 }
480
481 protected List<String> getImportScriptSql(File scriptFile) throws IOException {
482 List<String> lines = Files.readLines(scriptFile, Charsets.UTF_8);
483
484 List<String> result = Lists.newArrayListWithCapacity(lines.size());
485
486 Predicate<String> predicate = new Predicate<String>() {
487
488 Set<String> forbiddenStarts = Sets.newHashSet(
489 "SET ",
490 "CREATE USER ",
491 "CREATE SCHEMA ",
492 "GRANT DBA TO ");
493
494 @Override
495 public boolean apply(String input) {
496 boolean accept = true;
497 for (String forbiddenStart : forbiddenStarts) {
498 if (input.startsWith(forbiddenStart)) {
499 accept = false;
500 break;
501 }
502 }
503 return accept;
504 }
505 };
506 for (String line : lines) {
507 if (predicate.apply(line.trim().toUpperCase())) {
508 result.add(line);
509 }
510 }
511 return result;
512 }
513 }