diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java b/org.springframework.core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java
new file mode 100644
index 0000000000..dc20e6747f
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002-2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.core.io;
+
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link ResourceLoader} implementation that interprets plain resource paths
+ * as relative to a given java.lang.Class
.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0
+ * @see java.lang.Class#getResource(String)
+ * @see ClassPathResource#ClassPathResource(String, Class)
+ */
+public class ClassRelativeResourceLoader extends DefaultResourceLoader {
+
+ private final Class clazz;
+
+
+ /**
+ * Create a new ClassRelativeResourceLoader for the given class.
+ * @param clazz the class to load resources through
+ */
+ public ClassRelativeResourceLoader(Class clazz) {
+ Assert.notNull(clazz, "Class must not be null");
+ this.clazz = clazz;
+ setClassLoader(clazz.getClassLoader());
+ }
+
+ protected Resource getResourceByPath(String path) {
+ return new ClassRelativeContextResource(path, this.clazz);
+ }
+
+
+ /**
+ * ClassPathResource that explicitly expresses a context-relative path
+ * through implementing the ContextResource interface.
+ */
+ private static class ClassRelativeContextResource extends ClassPathResource implements ContextResource {
+
+ private final Class clazz;
+
+ public ClassRelativeContextResource(String path, Class clazz) {
+ super(path, clazz);
+ this.clazz = clazz;
+ }
+
+ public String getPathWithinContext() {
+ return getPath();
+ }
+
+ @Override
+ public Resource createRelative(String relativePath) {
+ String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
+ return new ClassRelativeContextResource(pathToUse, this.clazz);
+ }
+ }
+
+}
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/SimpleDriverDataSource.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/SimpleDriverDataSource.java
index 2185c0310b..b1972b29ae 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/SimpleDriverDataSource.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/SimpleDriverDataSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -107,8 +107,8 @@ public class SimpleDriverDataSource extends AbstractDriverBasedDataSource {
* within the SimpleDriverDataSource.
* @see #setDriver
*/
- public void setDriverClass(Class driverClass) {
- this.driver = (Driver) BeanUtils.instantiateClass(driverClass);
+ public void setDriverClass(Class extends Driver> driverClass) {
+ this.driver = BeanUtils.instantiateClass(driverClass);
}
/**
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/AbstractEmbeddedDatabaseConfigurer.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/AbstractEmbeddedDatabaseConfigurer.java
index 280d5f576b..634eb57cde 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/AbstractEmbeddedDatabaseConfigurer.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/AbstractEmbeddedDatabaseConfigurer.java
@@ -13,38 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.jdbc.datasource.embedded;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
-
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
- * Base class for {@link EmbeddedDatabaseConfigurer} implementations providing common shutdown behaviour.
+ * Base class for {@link EmbeddedDatabaseConfigurer} implementations providing common shutdown behavior.
+ *
* @author Oliver Gierke
+ * @author Juergen Hoeller
* @since 3.0
*/
abstract class AbstractEmbeddedDatabaseConfigurer implements EmbeddedDatabaseConfigurer {
- private static final Log logger = LogFactory.getLog(AbstractEmbeddedDatabaseConfigurer.class);
+ protected final Log logger = LogFactory.getLog(getClass());
public void shutdown(DataSource dataSource, String databaseName) {
- Connection connection = JdbcUtils.getConnection(dataSource);
- Statement stmt = null;
try {
- stmt = connection.createStatement();
+ Connection connection = dataSource.getConnection();
+ Statement stmt = connection.createStatement();
stmt.execute("SHUTDOWN");
- } catch (SQLException e) {
+ }
+ catch (SQLException ex) {
if (logger.isWarnEnabled()) {
- logger.warn("Could not shutdown embedded database", e);
+ logger.warn("Could not shutdown embedded database", ex);
}
- } finally {
- JdbcUtils.closeStatement(stmt);
}
}
+
}
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/CannotReadScriptException.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/CannotReadScriptException.java
index fe0d703bee..dd2ae82ff9 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/CannotReadScriptException.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/CannotReadScriptException.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.jdbc.datasource.embedded;
import org.springframework.core.io.support.EncodedResource;
@@ -20,19 +21,19 @@ import org.springframework.core.io.support.EncodedResource;
/**
* Thrown by {@link ResourceDatabasePopulator} if one of its SQL scripts could
* not be read during population.
+ *
* @author Keith Donald
* @since 3.0
*/
-@SuppressWarnings("serial")
public class CannotReadScriptException extends RuntimeException {
/**
- * Creates a new cannot read script exception.
- *
+ * Constructor a new CannotReadScriptException.
* @param resource the resource that could not be read from
* @param cause the underlying cause of the resource access failure
*/
public CannotReadScriptException(EncodedResource resource, Throwable cause) {
super("Cannot read SQL script from " + resource, cause);
}
+
}
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/ConnectionProperties.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/ConnectionProperties.java
index d4c7ee0a61..a28d7dee1c 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/ConnectionProperties.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/ConnectionProperties.java
@@ -13,38 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.jdbc.datasource.embedded;
+import java.sql.Driver;
+
/**
* DataSourceFactory helper that allows essential JDBC connection properties to be configured consistently,
* independent of the actual DataSource implementation.
+ *
* @author Keith Donald
* @since 3.0
* @see DataSourceFactory
*/
public interface ConnectionProperties {
-
+
/**
* Set the JDBC driver to use to connect to the database.
* @param driverClass the jdbc driver class
*/
- void setDriverClass(Class> driverClass);
-
+ void setDriverClass(Class driverClass);
+
/**
* Sets the JDBC connection URL of the database.
* @param url the connection url
*/
void setUrl(String url);
-
+
/**
* Sets the username to use to connect to the database.
* @param username the username
*/
void setUsername(String username);
-
+
/**
* Sets the password to use to connect to the database.
* @param password the password
*/
void setPassword(String password);
+
}
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DataSourceFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DataSourceFactory.java
index ea4789b9ec..9df27cf6b0 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DataSourceFactory.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DataSourceFactory.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.jdbc.datasource.embedded;
import javax.sql.DataSource;
@@ -20,21 +21,25 @@ import javax.sql.DataSource;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
/**
- * Encapsulates the creation of a particular DataSource implementation, such as a {@link SimpleDriverDataSource} or connection pool such as Apache DBCP or c3p0.
- * Call {@link #getConnectionProperties()} to configure normalized DataSource properties before calling {@link #getDataSource()} to actually get the configured DataSource instance.
+ * Encapsulates the creation of a particular DataSource implementation, such as a
+ * {@link SimpleDriverDataSource} or connection pool such as Apache DBCP or C3P0.
+ *
+ *
Call {@link #getConnectionProperties()} to configure normalized DataSource properties + * before calling {@link #getDataSource()} to actually get the configured DataSource instance. + * * @author Keith Donald * @since 3.0 */ public interface DataSourceFactory { - + /** * Allows properties of the DataSource to be configured. */ ConnectionProperties getConnectionProperties(); - + /** * Returns the DataSource with the connection properties applied. */ DataSource getDataSource(); - + } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DatabasePopulator.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DatabasePopulator.java index dfa35940ce..e5a3bad750 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DatabasePopulator.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DatabasePopulator.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; import java.sql.Connection; @@ -20,6 +21,7 @@ import java.sql.SQLException; /** * Strategy used to populate an embedded database during initialization. + * * @author Keith Donald * @since 3.0 * @see ResourceDatabasePopulator @@ -32,4 +34,5 @@ public interface DatabasePopulator { * @throws SQLException if an unrecoverable data access exception occurs during database population */ void populate(Connection connection) throws SQLException; -} \ No newline at end of file + +} diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DerbyEmbeddedDatabaseConfigurer.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DerbyEmbeddedDatabaseConfigurer.java index d400f7f7d3..8df66a239d 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DerbyEmbeddedDatabaseConfigurer.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/DerbyEmbeddedDatabaseConfigurer.java @@ -13,26 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.sql.Connection; import java.sql.SQLException; - +import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.derby.impl.io.VFMemoryStorageFactory; import org.apache.derby.jdbc.EmbeddedDriver; -import org.springframework.jdbc.datasource.SimpleDriverDataSource; -import org.springframework.util.ClassUtils; /** * {@link EmbeddedDatabaseConfigurer} for the Apache Derby database. + * * @author Oliver Gierke + * @author Juergen Hoeller * @since 3.0 */ final class DerbyEmbeddedDatabaseConfigurer implements EmbeddedDatabaseConfigurer { @@ -46,9 +46,6 @@ final class DerbyEmbeddedDatabaseConfigurer implements EmbeddedDatabaseConfigure private static DerbyEmbeddedDatabaseConfigurer INSTANCE; - private DerbyEmbeddedDatabaseConfigurer() { - } - /** * Get the singleton {@link DerbyEmbeddedDatabaseConfigurer} instance. * @return the configurer @@ -59,50 +56,48 @@ final class DerbyEmbeddedDatabaseConfigurer implements EmbeddedDatabaseConfigure // disable log file System.setProperty("derby.stream.error.method", DerbyEmbeddedDatabaseConfigurer.class.getName() + ".getNoopOutputStream"); - ClassUtils.forName("org.apache.derby.jdbc.EmbeddedDriver", DerbyEmbeddedDatabaseConfigurer.class - .getClassLoader()); INSTANCE = new DerbyEmbeddedDatabaseConfigurer(); } return INSTANCE; } + private DerbyEmbeddedDatabaseConfigurer() { + } + public void configureConnectionProperties(ConnectionProperties properties, String databaseName) { - properties.setDriverClass(org.apache.derby.jdbc.EmbeddedDriver.class); + properties.setDriverClass(EmbeddedDriver.class); properties.setUrl(String.format(URL_TEMPLATE, databaseName, "create=true")); properties.setUsername("sa"); properties.setPassword(""); } public void shutdown(DataSource dataSource, String databaseName) { - Connection connection = null; try { - SimpleDriverDataSource shutdownDataSource = new SimpleDriverDataSource(); - shutdownDataSource.setDriverClass(EmbeddedDriver.class); - shutdownDataSource.setUrl(String.format(URL_TEMPLATE, databaseName, "shutdown=true")); - connection = shutdownDataSource.getConnection(); - } catch (SQLException e) { - if (SHUTDOWN_CODE.equals(e.getSQLState())) { + new EmbeddedDriver().connect( + String.format(URL_TEMPLATE, databaseName, "shutdown=true"), new Properties()); + } + catch (SQLException ex) { + if (SHUTDOWN_CODE.equals(ex.getSQLState())) { purgeDatabase(databaseName); - } else { - logger.warn("Could not shutdown in-memory Derby database", e); } - } finally { - JdbcUtils.closeConnection(connection); + else { + logger.warn("Could not shutdown in-memory Derby database", ex); + } } } /** - * Purges the in-memory database, to prevent it from hanging around after - * being shut down - * @param databaseName + * Purge the in-memory database, to prevent it from hanging around after + * being shut down. */ private void purgeDatabase(String databaseName) { // TODO: update this code once Derby adds a proper way to remove an in-memory db // (see http://wiki.apache.org/db-derby/InMemoryBackEndPrimer for details) try { VFMemoryStorageFactory.purgeDatabase(new File(databaseName).getCanonicalPath()); - } catch (IOException ioe) { - logger.warn("Could not purge in-memory Derby database", ioe); + } + catch (IOException ex) { + logger.warn("Could not purge in-memory Derby database", ex); } } @@ -114,9 +109,9 @@ final class DerbyEmbeddedDatabaseConfigurer implements EmbeddedDatabaseConfigure static OutputStream getNoopOutputStream() { return new OutputStream() { public void write(int b) throws IOException { - // ignore the input + // ignore the output } }; } -} \ No newline at end of file +} diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabase.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabase.java index 6a01632c79..634fa5c8fb 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabase.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabase.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; import javax.sql.DataSource; @@ -21,13 +22,15 @@ import javax.sql.DataSource; * A handle to an EmbeddedDatabase instance. * Is a {@link DataSource}. * Adds a shutdown operation so the embedded database instance can be shutdown. + * * @author Keith Donald * @since 3.0 */ public interface EmbeddedDatabase extends DataSource { - + /** * Shutdown this embedded database. */ void shutdown(); + } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java index adfa2252bd..9f1989ff6a 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.java @@ -13,39 +13,55 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; -import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.ClassRelativeResourceLoader; import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; /** * A builder that provides a fluent API for constructing an embedded database. - * Usage example: + * + *
Usage example: *
* EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); * EmbeddedDatabase db = builder.script("schema.sql").script("test-data.sql").build(); * db.shutdown(); *+ * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ public class EmbeddedDatabaseBuilder { - private EmbeddedDatabaseFactory databaseFactory; + private final EmbeddedDatabaseFactory databaseFactory; - private ResourceDatabasePopulator databasePopulator; + private final ResourceDatabasePopulator databasePopulator; + + private final ResourceLoader resourceLoader; + - private ResourceLoader resourceLoader; - /** - * Creates a new embedded database builder. + * Create a new embedded database builder. */ public EmbeddedDatabaseBuilder() { - init(new DefaultResourceLoader()); + this(new DefaultResourceLoader()); + } + + /** + * Create a new embedded database builder withfor the given ResourceLoader. + * @param resourceLoader the ResourceLoader to delegate to + */ + public EmbeddedDatabaseBuilder(ResourceLoader resourceLoader) { + this.databaseFactory = new EmbeddedDatabaseFactory(); + this.databasePopulator = new ResourceDatabasePopulator(); + this.databaseFactory.setDatabasePopulator(this.databasePopulator); + this.resourceLoader = resourceLoader; } + /** * Sets the name of the embedded database * Defaults to 'testdb' if not called. @@ -53,7 +69,7 @@ public class EmbeddedDatabaseBuilder { * @return this, for fluent call chaining */ public EmbeddedDatabaseBuilder name(String databaseName) { - databaseFactory.setDatabaseName(databaseName); + this.databaseFactory.setDatabaseName(databaseName); return this; } @@ -64,7 +80,7 @@ public class EmbeddedDatabaseBuilder { * @return this, for fluent call chaining */ public EmbeddedDatabaseBuilder type(EmbeddedDatabaseType databaseType) { - databaseFactory.setDatabaseType(databaseType); + this.databaseFactory.setDatabaseType(databaseType); return this; } @@ -74,7 +90,7 @@ public class EmbeddedDatabaseBuilder { * @return this, for fluent call chaining */ public EmbeddedDatabaseBuilder script(String sqlResource) { - databasePopulator.addScript(resourceLoader.getResource(sqlResource)); + this.databasePopulator.addScript(resourceLoader.getResource(sqlResource)); return this; } @@ -83,45 +99,28 @@ public class EmbeddedDatabaseBuilder { * @return the embedded database */ public EmbeddedDatabase build() { - return databaseFactory.getDatabase(); - } - - /** - * Factory method that creates a EmbeddedDatabaseBuilder that loads SQL resources relative to the provided class. - * @param clazz the class to load relative to - * @return the embedded database builder - */ - public static EmbeddedDatabaseBuilder relativeTo(final Class> clazz) { - ResourceLoader loader = new ResourceLoader() { - public ClassLoader getClassLoader() { - return getClass().getClassLoader(); - } - - public Resource getResource(String location) { - return new ClassPathResource(location, clazz); - } - }; - return new EmbeddedDatabaseBuilder(loader); + return this.databaseFactory.getDatabase(); } - + + /** * Factory method that builds a default EmbeddedDatabase instance. - * The default instance is HSQL with a schema created from classpath:schema.sql and test-data loaded from classpath:test-data.sql. + * The default instance is HSQL with a schema created from "classpath:schema.sql" + * and test-data loaded from "classpath:test-data.sql". * @return an embedded database */ public static EmbeddedDatabase buildDefault() { return new EmbeddedDatabaseBuilder().script("schema.sql").script("test-data.sql").build(); } - - private EmbeddedDatabaseBuilder(ResourceLoader loader) { - init(loader); - } - - private void init(ResourceLoader loader) { - databaseFactory = new EmbeddedDatabaseFactory(); - databasePopulator = new ResourceDatabasePopulator(); - databaseFactory.setDatabasePopulator(databasePopulator); - resourceLoader = loader; + + /** + * Factory method that creates a EmbeddedDatabaseBuilder that loads SQL resources + * relative to the provided class. + * @param clazz the class to load relative to + * @return the embedded database builder + */ + public static EmbeddedDatabaseBuilder relativeTo(Class clazz) { + return new EmbeddedDatabaseBuilder(new ClassRelativeResourceLoader(clazz)); } } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurer.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurer.java index 3b2cea33ad..f82e0be500 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurer.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurer.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; import javax.sql.DataSource; @@ -20,6 +21,7 @@ import javax.sql.DataSource; /** * Encapsulates the configuration required to create, connect to, and shutdown a specific type of embedded database such as HSQL or H2. * Create a implementation for each database type you wish to support; for example HSQL, H2, or some other type. + * * @author Keith Donald * @since 3.0 */ diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurerFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurerFactory.java index 4c4bbfbd59..8719cc8ab2 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurerFactory.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseConfigurerFactory.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; import org.springframework.util.Assert; /** - * Maps well-known {@link EmbeddedDatabaseType embedded database types} to {@link EmbeddedDatabaseConfigurer} - * strategies. + * Maps well-known {@link EmbeddedDatabaseType embedded database types} to + * {@link EmbeddedDatabaseConfigurer} strategies. + * * @author Keith Donald * @author Oliver Gierke * @since 3.0 @@ -27,25 +29,26 @@ import org.springframework.util.Assert; final class EmbeddedDatabaseConfigurerFactory { public static EmbeddedDatabaseConfigurer getConfigurer(EmbeddedDatabaseType type) throws IllegalStateException { - Assert.notNull(type, "The EmbeddedDatabaseType is required"); + Assert.notNull(type, "EmbeddedDatabaseType is required"); try { switch (type) { - case HSQL: - return HsqlEmbeddedDatabaseConfigurer.getInstance(); - case H2: - return H2EmbeddedDatabaseConfigurer.getInstance(); - case DERBY: - return DerbyEmbeddedDatabaseConfigurer.getInstance(); - default: - throw new UnsupportedOperationException("Other embedded database types not yet supported"); + case HSQL: + return HsqlEmbeddedDatabaseConfigurer.getInstance(); + case H2: + return H2EmbeddedDatabaseConfigurer.getInstance(); + case DERBY: + return DerbyEmbeddedDatabaseConfigurer.getInstance(); + default: + throw new UnsupportedOperationException("Other embedded database types not yet supported"); } - } catch (ClassNotFoundException e) { - throw new IllegalStateException("Drivers for test database type [" + type - + "] are not available in the classpath", e); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException("Driver for test database type [" + type + + "] is not available in the classpath", ex); } } private EmbeddedDatabaseConfigurerFactory() { } -} \ No newline at end of file +} diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java index 334d806c32..12c78d0d33 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseFactory.java @@ -13,39 +13,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.jdbc.datasource.embedded; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; - import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.util.Assert; /** * Creates a {@link EmbeddedDatabase} instance. Callers are guaranteed that the returned database has been fully * initialized and populated. - *
- * Can be configured:
+ *
+ *
Can be configured:
* Call {@link #setDatabaseName(String)} to change the name of the database.
* Call {@link #setDatabaseType(EmbeddedDatabaseType)} to set the database type if you wish to use one of the supported types.
* Call {@link #setDatabaseConfigurer(EmbeddedDatabaseConfigurer)} to configure support for your own embedded database type.
* Call {@link #setDatabasePopulator(DatabasePopulator)} to change the algorithm used to populate the database.
* Call {@link #setDataSourceFactory(DataSourceFactory)} to change the type of DataSource used to connect to the database.
* Call {@link #getDatabase()} to get the {@link EmbeddedDatabase} instance.
+ *
* @author Keith Donald
+ * @author Juergen Hoeller
* @since 3.0
*/
public class EmbeddedDatabaseFactory {
private static Log logger = LogFactory.getLog(EmbeddedDatabaseFactory.class);
- private String databaseName;
+ private String databaseName = "testdb";
- private DataSourceFactory dataSourceFactory;
+ private DataSourceFactory dataSourceFactory = new SimpleDriverDataSourceFactory();
private EmbeddedDatabaseConfigurer databaseConfigurer;
@@ -53,101 +57,114 @@ public class EmbeddedDatabaseFactory {
private DataSource dataSource;
- /**
- * Creates a default {@link EmbeddedDatabaseFactory}.
- * Calling {@link #getDatabase()} will create a embedded HSQL database of name 'testdb'.
- */
- public EmbeddedDatabaseFactory() {
- setDatabaseName("testdb");
- setDatabaseType(EmbeddedDatabaseType.HSQL);
- setDataSourceFactory(new SimpleDriverDataSourceFactory());
- }
/**
- * Sets the name of the database. Defaults to 'testdb'.
- * @param name of the test database
+ * Set the name of the database. Defaults to "testdb".
+ * @param databaseName name of the test database
*/
- public void setDatabaseName(String name) {
- Assert.notNull(name, "The testDatabaseName is required");
- databaseName = name;
+ public void setDatabaseName(String databaseName) {
+ Assert.notNull(databaseName, "Database name is required");
+ this.databaseName = databaseName;
}
/**
- * Sets the type of embedded database to use. Call this when you wish to configure one of the pre-supported types.
- * Defaults to HSQL.
+ * Set the type of embedded database to use. Call this when you wish to configure
+ * one of the pre-supported types. Defaults to HSQL.
* @param type the test database type
*/
public void setDatabaseType(EmbeddedDatabaseType type) {
- setDatabaseConfigurer(EmbeddedDatabaseConfigurerFactory.getConfigurer(type));
+ this.databaseConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(type);
}
/**
- * Sets the strategy that will be used to configure the embedded database instance.
+ * Set the strategy that will be used to configure the embedded database instance.
* Call this when you wish to use an embedded database type not already supported.
* @param configurer the embedded database configurer
*/
public void setDatabaseConfigurer(EmbeddedDatabaseConfigurer configurer) {
+ Assert.notNull(configurer, "EmbeddedDatabaseConfigurer is required");
this.databaseConfigurer = configurer;
}
/**
- * Sets the strategy that will be used to populate the embedded database. Defaults to null.
+ * Set the strategy that will be used to populate the embedded database. Defaults to null.
* @param populator the database populator
*/
public void setDatabasePopulator(DatabasePopulator populator) {
- Assert.notNull(populator, "The DatabasePopulator is required");
- databasePopulator = populator;
+ Assert.notNull(populator, "DatabasePopulator is required");
+ this.databasePopulator = populator;
}
/**
- * Sets the factory to use to create the DataSource instance that connects to the embedded database
+ * Set the factory to use to create the DataSource instance that connects to the embedded database.
* Defaults to {@link SimpleDriverDataSourceFactory}.
* @param dataSourceFactory the data source factory
*/
public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
- Assert.notNull(dataSourceFactory, "The DataSourceFactory is required");
+ Assert.notNull(dataSourceFactory, "DataSourceFactory is required");
this.dataSourceFactory = dataSourceFactory;
}
- // other public methods
-
/**
* Factory method that returns the embedded database instance.
*/
public EmbeddedDatabase getDatabase() {
- if (dataSource == null) {
+ if (this.dataSource == null) {
initDatabase();
}
- return new EmbeddedDataSourceProxy(dataSource);
+ return new EmbeddedDataSourceProxy(this.dataSource);
}
- // subclassing hooks
/**
* Hook to initialize the embedded database. Subclasses may call to force initialization. After calling this method,
* {@link #getDataSource()} returns the DataSource providing connectivity to the db.
*/
protected void initDatabase() {
- // create the embedded database source first
+ // Create the embedded database source first
if (logger.isInfoEnabled()) {
- logger.info("Created embedded database '" + databaseName + "'");
+ logger.info("Creating embedded database '" + this.databaseName + "'");
}
- databaseConfigurer.configureConnectionProperties(dataSourceFactory.getConnectionProperties(), databaseName);
- dataSource = dataSourceFactory.getDataSource();
- if (databasePopulator != null) {
- // now populate the database
+ if (this.databaseConfigurer == null) {
+ this.databaseConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(EmbeddedDatabaseType.HSQL);
+ }
+ this.databaseConfigurer.configureConnectionProperties(
+ this.dataSourceFactory.getConnectionProperties(), this.databaseName);
+ this.dataSource = this.dataSourceFactory.getDataSource();
+
+ // Now populate the database
+ if (this.databasePopulator != null) {
populateDatabase();
}
}
+ private void populateDatabase() {
+ try {
+ Connection connection = this.dataSource.getConnection();
+ try {
+ this.databasePopulator.populate(connection);
+ }
+ finally {
+ try {
+ connection.close();
+ }
+ catch (SQLException ex) {
+ // ignore
+ }
+ }
+ }
+ catch (SQLException ex) {
+ throw new DataAccessResourceFailureException("Failed to populate database", ex);
+ }
+ }
+
/**
- * Hook that gets the datasource that provides the connectivity to the embedded database.
- * Returns null if the datasource has not been initialized or the database has been shutdown.
+ * Hook that gets the DataSource that provides the connectivity to the embedded database.
+ *
Returns null if the DataSource has not been initialized or the database has been shut down.
* Subclasses may call to access the datasource instance directly.
- * @return the datasource
*/
protected DataSource getDataSource() {
- return dataSource;
+ return this.dataSource;
}
/**
@@ -155,68 +172,56 @@ public class EmbeddedDatabaseFactory {
* After calling, {@link #getDataSource()} returns null. Does nothing if no embedded database has been initialized.
*/
protected void shutdownDatabase() {
- if (dataSource != null) {
- databaseConfigurer.shutdown(dataSource, databaseName);
- dataSource = null;
+ if (this.dataSource != null) {
+ this.databaseConfigurer.shutdown(this.dataSource, this.databaseName);
+ this.dataSource = null;
}
}
- // internal helper methods
-
- private void populateDatabase() {
- Connection connection = JdbcUtils.getConnection(dataSource);
- try {
- databasePopulator.populate(connection);
- } catch (SQLException e) {
- throw new RuntimeException("SQLException occurred populating embedded database", e);
- } finally {
- JdbcUtils.closeConnection(connection);
- }
- }
private class EmbeddedDataSourceProxy implements EmbeddedDatabase {
- private DataSource dataSource;
+
+ private final DataSource dataSource;
public EmbeddedDataSourceProxy(DataSource dataSource) {
this.dataSource = dataSource;
}
public Connection getConnection() throws SQLException {
- return dataSource.getConnection();
+ return this.dataSource.getConnection();
}
public Connection getConnection(String username, String password) throws SQLException {
- return dataSource.getConnection(username, password);
+ return this.dataSource.getConnection(username, password);
}
public int getLoginTimeout() throws SQLException {
- return dataSource.getLoginTimeout();
+ return this.dataSource.getLoginTimeout();
}
public PrintWriter getLogWriter() throws SQLException {
- return dataSource.getLogWriter();
+ return this.dataSource.getLogWriter();
}
public void setLoginTimeout(int seconds) throws SQLException {
- dataSource.setLoginTimeout(seconds);
+ this.dataSource.setLoginTimeout(seconds);
}
public void setLogWriter(PrintWriter out) throws SQLException {
- dataSource.setLogWriter(out);
+ this.dataSource.setLogWriter(out);
}
public boolean isWrapperFor(Class> iface) throws SQLException {
- return dataSource.isWrapperFor(iface);
+ return this.dataSource.isWrapperFor(iface);
}
public The target DataSource is returned instead of a {@link EmbeddedDatabase} proxy since the FactoryBean
+ * will manage the initialization and destruction lifecycle of the database instance.
+ *
+ * Implements DisposableBean to shutdown the embedded database when the managing Spring container is shutdown.
+ *
* @author Keith Donald
* @since 3.0
*/
-public class EmbeddedDatabaseFactoryBean extends EmbeddedDatabaseFactory implements FactoryBean
- * Call {@link #addScript(Resource)} to add a SQL script location. Call {@link #addScript(Resource)} to add a SQL script location.
+ *
+ *
* Call {@link #setSqlScriptEncoding(String)} to set the encoding for all added scripts.
+ *
* @author Keith Donald
* @since 3.0
*/
@@ -47,6 +50,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
private String sqlScriptEncoding;
+
/**
* Add a script to execute to populate the database.
* @param script the path to a SQL script
@@ -65,15 +69,17 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
/**
* Specify the encoding for SQL scripts, if different from the platform encoding.
- * Note setting this property has no effect on added scripts that are already {@link EncodedResource encoded resources}.
+ * Note setting this property has no effect on added scripts that are already
+ * {@link EncodedResource encoded resources}.
* @see #addScript(Resource)
*/
public void setSqlScriptEncoding(String sqlScriptEncoding) {
this.sqlScriptEncoding = sqlScriptEncoding;
}
-
+
+
public void populate(Connection connection) throws SQLException {
- for (Resource script : scripts) {
+ for (Resource script : this.scripts) {
executeSqlScript(connection, applyEncodingIfNecessary(script), false);
}
}
@@ -81,8 +87,9 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
private EncodedResource applyEncodingIfNecessary(Resource script) {
if (script instanceof EncodedResource) {
return (EncodedResource) script;
- } else {
- return new EncodedResource(script, sqlScriptEncoding);
+ }
+ else {
+ return new EncodedResource(script, this.sqlScriptEncoding);
}
}
@@ -95,6 +102,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
*/
private void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError)
throws SQLException {
+
if (logger.isInfoEnabled()) {
logger.info("Executing SQL script from " + resource);
}
@@ -112,25 +120,34 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
}
splitSqlScript(script, delimiter, statements);
int lineNumber = 0;
- for (String statement : statements) {
- lineNumber++;
- Statement stmt = null;
- try {
- stmt = connection.createStatement();
- int rowsAffected = stmt.executeUpdate(statement);
- if (logger.isDebugEnabled()) {
- logger.debug(rowsAffected + " rows affected by SQL: " + statement);
+ Statement stmt = connection.createStatement();
+ try {
+ for (String statement : statements) {
+ lineNumber++;
+ try {
+ int rowsAffected = stmt.executeUpdate(statement);
+ if (logger.isDebugEnabled()) {
+ logger.debug(rowsAffected + " rows affected by SQL: " + statement);
+ }
}
- } catch (SQLException e) {
- if (continueOnError) {
- if (logger.isWarnEnabled()) {
- logger.warn("Line " + lineNumber + " statement failed: " + statement, e);
+ catch (SQLException ex) {
+ if (continueOnError) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Line " + lineNumber + " statement failed: " + statement, ex);
+ }
+ }
+ else {
+ throw ex;
}
- } else {
- throw e;
}
- } finally {
- JdbcUtils.closeStatement(stmt);
+ }
+ }
+ finally {
+ try {
+ stmt.close();
+ }
+ catch (Throwable ex) {
+ logger.debug("Could not close JDBC Statement", ex);
}
}
long elapsedTime = System.currentTimeMillis() - startTime;
@@ -209,4 +226,4 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
}
}
-}
\ No newline at end of file
+}
diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/SimpleDriverDataSourceFactory.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/SimpleDriverDataSourceFactory.java
index 4f065d8f73..89d83823be 100644
--- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/SimpleDriverDataSourceFactory.java
+++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/datasource/embedded/SimpleDriverDataSourceFactory.java
@@ -13,35 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.jdbc.datasource.embedded;
+import java.sql.Driver;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
+import org.springframework.util.Assert;
/**
* Creates a {@link SimpleDriverDataSource}.
+ *
* @author Keith Donald
+ * @author Juergen Hoeller
* @since 3.0
*/
final class SimpleDriverDataSourceFactory implements DataSourceFactory {
- private SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
+ private final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
public ConnectionProperties getConnectionProperties() {
return new ConnectionProperties() {
- public void setDriverClass(Class> driverClass) {
- dataSource.setDriverClass(driverClass);
+ @SuppressWarnings("unchecked")
+ public void setDriverClass(Class driverClass) {
+ Assert.isAssignable(Driver.class, driverClass);
+ dataSource.setDriverClass((Class extends Driver>) driverClass);
}
-
public void setUrl(String url) {
dataSource.setUrl(url);
}
-
public void setUsername(String username) {
dataSource.setUsername(username);
}
-
public void setPassword(String password) {
dataSource.setPassword(password);
}
@@ -49,7 +53,7 @@ final class SimpleDriverDataSourceFactory implements DataSourceFactory {
}
public DataSource getDataSource() {
- return dataSource;
+ return this.dataSource;
}
}