001: /* DDSteps - Data Driven JUnit Test Steps
002: * Copyright (C) 2006 Jayway AB
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License version 2.1 as published by the Free Software Foundation.
007: *
008: * This library is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, visit
015: * http://www.opensource.org/licenses/lgpl-license.php
016: */
017: package org.ddsteps.fixture.dbunit;
018:
019: import javax.sql.DataSource;
020:
021: import junit.framework.TestCase;
022: import net.sf.ehcache.CacheException;
023: import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
024: import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
025:
026: import org.apache.commons.lang.Validate;
027: import org.dbunit.dataset.filter.ITableFilter;
028: import org.dbunit.dataset.filter.IncludeTableFilter;
029: import org.dbunit.operation.DatabaseOperation;
030: import org.ddsteps.DDStepsException;
031: import org.ddsteps.dbunit.DatabaseConnectionFactory;
032: import org.ddsteps.dbunit.StandardConnectionFactory;
033: import org.ddsteps.fixture.Fixture;
034: import org.ddsteps.fixture.FixtureLoader;
035: import org.springframework.beans.factory.InitializingBean;
036:
037: /**
038: * @author adamskogman
039: *
040: */
041: public abstract class DbUnitFixtureLoaderSupport implements
042: FixtureLoader, InitializingBean {
043:
044: /**
045: * Constant. Used as cache entry when there is no fixture.
046: */
047: protected final static String NULL_FIXTURE = "NULL";
048:
049: /**
050: * Field: Cache for loaded Excel DataSets.
051: */
052: protected SelfPopulatingCache cache;
053: /**
054: * Dependency: Database Connection Factory
055: */
056: protected DatabaseConnectionFactory connectionFactory;
057: /**
058: * Property: The setup operation. Default is clean insert.
059: */
060: protected DatabaseOperation setUpOperation = DatabaseOperation.CLEAN_INSERT;
061: /**
062: * Field: Table Filter. Not directly accessed, instead, using the (@link
063: * #setTableNames(String[])) will set this property.
064: */
065: protected ITableFilter tableFilter;
066:
067: /**
068: * Property: The tearDown operation. Default is none.
069: */
070: protected DatabaseOperation tearDownOperation = DatabaseOperation.NONE;
071:
072: /**
073: * Convenience, used to create the DatabaseConnectionFactory.
074: */
075: private DataSource dataSource;
076:
077: /**
078: * Convenience, used to create the DatabaseConnectionFactory.
079: */
080: private String schema;
081:
082: /**
083: */
084: public DbUnitFixtureLoaderSupport() {
085: super ();
086: }
087:
088: /**
089: * Make sure to call this from subclasses if you override.
090: *
091: * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
092: */
093: public void afterPropertiesSet() throws Exception {
094:
095: // Set up connection factory
096: if (connectionFactory == null) {
097: // Try creating the default using a datasource
098: if (dataSource == null) {
099: throw new IllegalStateException(
100: "You must set either ConnectionFactory or DataSource.");
101: }
102:
103: StandardConnectionFactory standardConnectionFactory = new StandardConnectionFactory(
104: dataSource);
105: standardConnectionFactory.setSchema(schema);
106:
107: this .connectionFactory = standardConnectionFactory;
108: }
109:
110: // Cache
111: if (cache == null) {
112: try {
113: cache = new SelfPopulatingCache(getCacheName(),
114: getCacheEntryFactory());
115: } catch (CacheException e) {
116: throw new DDStepsException(
117: "Could not create cache with name '"
118: + getCacheName() + "' for DataSet:s.",
119: e);
120: }
121: }
122:
123: }
124:
125: /**
126: * HOOK: Implement this.
127: *
128: * @return The name of the cache (subclass specific).
129: */
130: protected abstract CacheEntryFactory getCacheEntryFactory();
131:
132: /**
133: * HOOK: Implement this.
134: *
135: * @return The name of the cache (subclass specific).
136: */
137: protected abstract String getCacheName();
138:
139: /**
140: * @return Returns the setUpOperation.
141: */
142: public DatabaseOperation getSetUpOperation() {
143: return setUpOperation;
144: }
145:
146: /**
147: * The default Database operation to use when tearing down the fixture.
148: * Default is NONE, i.e. fixture is not torn down. See DbUnit.org for more
149: * information.
150: *
151: * @return Returns the tearDownOperation.
152: */
153: public DatabaseOperation getTearDownOperation() {
154: return tearDownOperation;
155: }
156:
157: /**
158: * The default Database operation to use when setting up the fixture.
159: * Default is CLEAN INSERT. See DbUnit.org for more information.
160: *
161: * @param setUpOperation
162: * The setUpOperation to set.
163: */
164: public void setSetUpOperation(DatabaseOperation setUpOperation) {
165: this .setUpOperation = setUpOperation;
166: }
167:
168: /**
169: * Set the table names to include in the fixtures loaded by this loader.
170: * Default is to include all tables.
171: * <p>
172: * Since this loader loades the SAME Excel file as the DdTestCase (and other
173: * DD test-case base-classes) you need to set this to make it clear which
174: * sheets are test data and which are fixture data.
175: * <p>
176: * Also, if you have TWO databases, you can create two different fixture
177: * loaders, and just differentiate them by having them load different
178: * tables.
179: * <p>
180: * Since these table names will probably come for a Spring context XML file,
181: * the table names are trimmed (whitespace removed) before they are used.
182: *
183: * @param tableNames
184: */
185: public void setTableNames(String[] tableNames) {
186:
187: Validate.notNull(tableNames,
188: "Argument tableNames must not be null");
189: Validate.noNullElements(tableNames,
190: "Argument tableNames must not contain null elements.");
191:
192: for (int i = 0; i < tableNames.length; i++) {
193: tableNames[i] = tableNames[i].trim();
194: }
195:
196: tableFilter = new IncludeTableFilter(tableNames);
197:
198: }
199:
200: /**
201: * @param tearDownOperation
202: * The tearDownOperation to set.
203: */
204: public void setTearDownOperation(DatabaseOperation tearDownOperation) {
205: this .tearDownOperation = tearDownOperation;
206: }
207:
208: /**
209: * Loads an DbUnit fixture from an file in the same package and with the
210: * same name (except for the suffix) as the testcase given the suffix.
211: * <p>
212: * I.e. if the tet case is <code>com.example.ftest.MyTest</code> the excel
213: * file /com/example/ftest/MyTest.suffix will be loaded.
214: *
215: * @param testCase
216: * @param suffix
217: * String suffix, excluding the .
218: * @return A fixture or null.
219: *
220: * @see org.ddsteps.fixture.FixtureLoader#loadFixture(junit.framework.TestCase)
221: */
222: public Fixture loadFixture(TestCase testCase, String suffix) {
223:
224: // the cache key is the full path of the Excel file.
225: String classNameWithPath = testCase.getClass().getName();
226: String fileName = "/" + classNameWithPath.replace('.', '/')
227: + "." + suffix;
228:
229: try {
230: Object entry = cache.get(fileName);
231:
232: if (entry != null && entry != NULL_FIXTURE) {
233: // If it existed, it can be cast
234: Fixture fixture = (Fixture) entry;
235:
236: return fixture;
237: }
238:
239: // No fixture, return null
240: return null;
241:
242: } catch (CacheException e) {
243: throw new DDStepsException("Could not get Fixture for "
244: + classNameWithPath, e);
245: }
246:
247: }
248:
249: /**
250: * @return Returns the connectionFactory.
251: */
252: public DatabaseConnectionFactory getConnectionFactory() {
253: return connectionFactory;
254: }
255:
256: /**
257: * Use this to set the whole connection factory at once. The alternative is
258: * to set both DataSource and Schema.
259: * <p>
260: * Must be called before (@link #afterPropertiesSet()).
261: *
262: * @param connectionFactory
263: * The connectionFactory to set.
264: */
265: public void setConnectionFactory(
266: DatabaseConnectionFactory connectionFactory) {
267: this .connectionFactory = connectionFactory;
268: }
269:
270: /**
271: * Convenience setter. Use this instead of (@link
272: * #setConnectionFactory(DatabaseConnectionFactory)) if you have a
273: * DataSource.
274: * <p>
275: * Must be called before (@link #afterPropertiesSet()).
276: *
277: * @param dataSource
278: * The DataSource
279: *
280: */
281: public void setDataSource(DataSource dataSource) {
282: this .dataSource = dataSource;
283: }
284:
285: /**
286: * @return Returns the cache.
287: */
288: public SelfPopulatingCache getCache() {
289: return cache;
290: }
291:
292: /**
293: * @return Returns the schema.
294: */
295: public String getSchema() {
296: return schema;
297: }
298:
299: /**
300: * @param schema
301: * The schema to set.
302: */
303: public void setSchema(String schema) {
304: this .schema = schema;
305: }
306:
307: /**
308: * @return Returns the dataSource.
309: */
310: public DataSource getDataSource() {
311: return dataSource;
312: }
313:
314: }
|