Java tutorial
/*********************************************************************************************************************** * 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; } }