/*********************************************************************************************************************** | |
* Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying | |
* materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, | |
* and is available at http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: Michael Breidenband (brox IT Solutions GmbH) - initial creator | |
**********************************************************************************************************************/ | |
package org.eclipse.smila.connectivity.framework.crawler.jdbc.test; | |
import java.io.File; | |
import java.sql.Connection; | |
import java.sql.Date; | |
import java.sql.DriverManager; | |
import java.sql.PreparedStatement; | |
import java.sql.SQLException; | |
import java.sql.Statement; | |
import java.util.ArrayList; | |
import java.util.Properties; | |
import junit.framework.TestCase; | |
import org.apache.commons.io.FileUtils; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.eclipse.smila.connectivity.framework.crawler.jdbc.JdbcCrawler; | |
/** | |
* Abstract base-class for Tests of the {@link JdbcCrawler}-class. Provides a database with a well defined state which | |
* can be used by extending Classes.<br> | |
* <br> | |
* | |
*/ | |
public abstract class AbstractDataEnabledJdbcCrawlerTestCase extends TestCase { | |
/** Timeout for service detection. */ | |
protected static final long WAIT_FOR_SERVICE_DELAY = 30000; | |
/** The name of the JDBC-driver to use. */ | |
protected static final String DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; | |
/** The name of the database to use. */ | |
protected static final String DB_NAME = "crawlerTestDerbyDB"; | |
/** Time to wait in ms for the crawler to initialize (used by subclasses). */ | |
protected static final int WAIT_FOR_CRAWLER_INIT = 1000; | |
/** | |
* The Connection URL to use. Notice that this includes the "create=true"-flag which causes the Derby engine to create | |
* the database files for us. | |
*/ | |
protected static final String CONNECTION_URL = "jdbc:derby:" + DB_NAME + ";create=true"; | |
/** The URL used for shutting down the entire embedded Derby engine. */ | |
protected static final String SHUTDOWN_URL = "jdbc:derby:;shutdown=true"; | |
/** Path to the resources-folder containing the file resources needed for creation of the database. */ | |
protected static final String RES_FOLDER_PATH = "configuration/res/"; | |
/** Name of the image file used as input for BLOB fields. */ | |
protected static final String PHOTO_FILE_NAME = "photo.png"; | |
/** Name of the text file used as input for CLOB fields. */ | |
protected static final String CV_FILE_NAME = "cv.txt"; | |
/** Constant specifying how many records to insert into the database fixture. */ | |
protected static final int RECORDS_TO_INSERT = 100; | |
/** Column-Index constant. */ | |
private static final int COLUMN_CV = 14; | |
/** Column-Index constant. */ | |
private static final int COLUMN_DOWNSIZED = 12; | |
/** Column-Index constant. */ | |
private static final int COLUMN_SCHEDULED_FOR_DOWNSIZING = 11; | |
/** Column-Index constant. */ | |
private static final int COLUMN_BIRTHDAY = 10; | |
/** Column-Index constant. */ | |
private static final int COLUMN_VACATIONDAYS = 9; | |
/** Column-Index constant. */ | |
private static final int COLUMN_BMI = 8; | |
/** Column-Index constant. */ | |
private static final int COLUMN_PHONE = 7; | |
/** Column-Index constant. */ | |
private static final int COLUMN_CITY = 6; | |
/** Column-Index constant. */ | |
private static final int COLUMN_PLZ = 5; | |
/** Column-Index constant. */ | |
private static final int COLUMN_STREET = 4; | |
/** Column-Index constant. */ | |
private static final int COLUMN_SURNAME = 3; | |
/** Column-Index constant. */ | |
private static final int COLUMN_FIRSTNAME = 2; | |
/** Column-Index constant. */ | |
private static final int COLUMN_ID = 1; | |
/** Column-Index constant. */ | |
private static final int COLUMN_PHOTO = 13; | |
/** Constant. */ | |
private static final int MAX_VACATIONDAYS = 2; | |
/** Constant. */ | |
private static final int DIGITS_IN_EXTENSION = 8; | |
/** Constant. */ | |
private static final int DIGITS_IN_AREA_CODE = 4; | |
/** Constant. */ | |
private static final int DIGITS_IN_PLZ = 5; | |
/** The {@link JdbcCrawler}-instance to be tested. */ | |
protected JdbcCrawler _crawler; | |
/** The Log - guess what: we need it for logging messages and errors. */ | |
protected final Log _log = LogFactory.getLog(getClass()); | |
/** | |
* {@inheritDoc} Called by the JUnit-Runner before execution of a testMethod of inheriting classes. Sets up a Database | |
* fixture by performing the following steps: | |
* <ol> | |
* <li>Shutdown a (potentially) running Derby engine by calling {@link DriverManager#getConnection(String)} with the | |
* Shutdown-URL (see {@link #SHUTDOWN_URL}).</li> | |
* <li>Delete all database files (potentially) remaining from prior test cases.</li> | |
* <li>Configure Derby engine to log all executed SQL in the log file and to rather append to an existing logfile than | |
* to overwrite it.</li> | |
* <li>Get a {@link Connection} to the Derby DB and insert 100 rows of data (see source code for details). This | |
* includes BLOB (from image file) and CLOB (from text file) fields.</li> | |
* <li>Release allocated JDBC-resources (Statement, Connection).</li> | |
* <li>Shutdown Derby Engine via Shutdown-URL (so the Crawler can start it up as it would normally)</li> | |
* <li>Instantiates a {@link JdbcCrawler}.</li> | |
* </ol> | |
* | |
* @see junit.framework.TestCase#setUp() | |
*/ | |
@Override | |
protected void setUp() throws Exception { | |
super.setUp(); | |
Class.forName(DRIVER_NAME).newInstance(); | |
// shutdown embedded Derby engine (if running) | |
// using SHUTDOWN_URL *always* results in SQLException, so we catch and ignore ... | |
try { | |
DriverManager.getConnection(SHUTDOWN_URL); | |
} catch (final SQLException e) { | |
_log.info("Testcase Setup: Shutting down Derby Engine"); | |
} | |
// delete existing db files | |
final File dbDirectory = new File(DB_NAME); | |
if (FileUtils.deleteQuietly(dbDirectory)) { | |
_log.info("Deleted DB files of [" + DB_NAME + "] database"); | |
} else { | |
_log.warn("Could not delete DB files of [" + DB_NAME + "] database"); | |
} | |
Class.forName(DRIVER_NAME).newInstance(); | |
final Properties p = System.getProperties(); | |
// we want to see all sql in the db log file | |
p.put("derby.language.logStatementText", "true"); | |
// we don't want the logfile to be recreated each time the engine starts ... | |
p.put("derby.infolog.append", "true"); | |
Connection connection = DriverManager.getConnection(CONNECTION_URL); | |
final ArrayList<Statement> statements = new ArrayList<Statement>(); // list of Statements, | |
// PreparedStatements | |
PreparedStatement psInsert = null; | |
Statement createStatement = null; | |
createStatement = connection.createStatement(); | |
statements.add(createStatement); | |
// create a person table... | |
createStatement | |
.execute("CREATE TABLE person(id int, vorname varchar(40), name varchar(40), strasse varchar(40), " | |
+ "plz varchar(5), ort varchar(40), festnetz varchar(20), body_mass_index double, vacationdays " | |
+ "integer, birthday date, scheduled_for_downsizing smallint, downsized timestamp, photo blob, cv clob)"); | |
_log.info("Created TABLE [person]"); | |
// insert 100 records ... | |
psInsert = connection.prepareStatement("INSERT INTO person VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)"); | |
statements.add(psInsert); | |
// prepare resource files for blob / clob insertion... | |
final File resDir = new File(RES_FOLDER_PATH); | |
final File photoFile = new File(resDir, PHOTO_FILE_NAME); | |
final File cvFile = new File(resDir, CV_FILE_NAME); | |
for (int i = 1; i <= RECORDS_TO_INSERT; i++) { | |
psInsert.setInt(COLUMN_ID, i); | |
psInsert.setString(COLUMN_FIRSTNAME, "Mustervorname" + i); | |
psInsert.setString(COLUMN_SURNAME, "Mustername" + i); | |
psInsert.setString(COLUMN_STREET, "Musterstrasse " + i); | |
psInsert.setString(COLUMN_PLZ, String.valueOf(getRandomInteger(DIGITS_IN_PLZ))); | |
psInsert.setString(COLUMN_CITY, "Musterstadt" + i); | |
psInsert.setString(COLUMN_PHONE, "0" + getRandomInteger(DIGITS_IN_AREA_CODE) + "-" | |
+ getRandomInteger(DIGITS_IN_EXTENSION)); | |
psInsert.setDouble(COLUMN_BMI, (Math.random() / Math.random())); | |
psInsert.setLong(COLUMN_VACATIONDAYS, getRandomInteger(MAX_VACATIONDAYS)); | |
psInsert.setDate(COLUMN_BIRTHDAY, new Date(new java.util.Date().getTime())); | |
psInsert.setBoolean(COLUMN_SCHEDULED_FOR_DOWNSIZING, ((getRandomInteger(1) % 2) == 0)); | |
psInsert.setDate(COLUMN_DOWNSIZED, new Date(new java.util.Date().getTime())); | |
psInsert.setBytes(COLUMN_PHOTO, FileUtils.readFileToByteArray(photoFile)); | |
psInsert.setString(COLUMN_CV, FileUtils.readFileToString(cvFile)); | |
psInsert.execute(); | |
} | |
// release all open resources to avoid unnecessary memory usage | |
for (final Statement st : statements) { | |
try { | |
st.close(); | |
} catch (final SQLException sqle) { | |
_log.error("Could not release Statement", sqle); | |
} | |
} | |
statements.clear(); | |
// Connection | |
try { | |
if (connection != null) { | |
connection.close(); | |
connection = null; | |
} | |
} catch (final SQLException sqle) { | |
_log.error("Could not release Connection", sqle); | |
} | |
// shutdown Derby engine AGAIN, so the Crawler can start it up as it would normally | |
try { | |
DriverManager.getConnection(SHUTDOWN_URL); | |
} catch (final SQLException e) { | |
_log.info("Testcase Setup: Shutting down Derby Engine"); | |
} | |
_crawler = new JdbcCrawler(); | |
} | |
/** | |
* tearDown()-Implementation. Waits for the Producing-Thread of the Crawler to die in order to prevent other test | |
* cases from starting (as they would causes issues when trying to re-setup the database fixture themselves) and | |
* finally closes the crawler. {@inheritDoc} | |
* | |
* @see junit.framework.TestCase#tearDown() | |
*/ | |
@Override | |
protected void tearDown() throws Exception { | |
_log.debug("Waiting for Crawling Thread to terminate ..."); | |
final Thread thread = _crawler.getProducerThread(); | |
if (thread != null && thread.isAlive()) { | |
thread.join(); | |
} | |
_log.debug("Crawling Thread terminated"); | |
_crawler.close(); | |
_log.debug("Crawler closed"); | |
_crawler = null; | |
_log.debug("Crawler object dereferenced"); | |
} | |
/** | |
* Used for generating random numbers for the data to insert into the db fixture. | |
* | |
* @param numberOfDigits | |
* Number of digits the generated {@code int} is supposed to have. | |
* @return A random {@code int}-value with the specified number of (decimal) digits. | |
*/ | |
private int getRandomInteger(final int numberOfDigits) { | |
float f = (float) Math.random(); | |
for (int i = 0; i < numberOfDigits; i++) { | |
// CHECKSTYLE:OFF | |
// Seriously, I dont't want to create a constant for this literal of 10 ... | |
f = f * 10; | |
// CHECKSTYLE:ON | |
} | |
final int i = Math.round(f); | |
return i; | |
} | |
} |