This is an automated email from the ASF dual-hosted git repository.
cbrisson pushed a commit to branch VELOCITY-965
in repository https://gitbox.apache.org/repos/asf/velocity-engine.git
The following commit(s) were added to refs/heads/VELOCITY-965 by this push:
new 7d98e5ef Complete refactoring of database objects handling in
DataSourceResourceLoader
7d98e5ef is described below
commit 7d98e5ef890e1f758532ee25df9ef67499dfc37e
Author: Claude Brisson <[email protected]>
AuthorDate: Wed Aug 28 12:05:12 2024 +0200
Complete refactoring of database objects handling in
DataSourceResourceLoader
---
.../loader/CachingDatabaseObjectsFactory.java | 153 +++++++++++
.../resource/loader/DataSourceResourceLoader.java | 291 +++++++--------------
.../resource/loader/DatabaseObjectsFactory.java | 39 +++
.../loader/DefaultDatabaseObjectsFactory.java | 53 ++++
.../test/sql/DataSourceResourceLoaderTestCase.java | 22 +-
...sourceResourceLoaderCachingFactoryTestCase.java | 19 ++
6 files changed, 376 insertions(+), 201 deletions(-)
diff --git
a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/CachingDatabaseObjectsFactory.java
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/CachingDatabaseObjectsFactory.java
new file mode 100644
index 00000000..57999610
--- /dev/null
+++
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/CachingDatabaseObjectsFactory.java
@@ -0,0 +1,153 @@
+package org.apache.velocity.runtime. resource.loader;
+
+import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.apache.velocity.util.ExtProperties;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * <p>Database objects factory which will keep a single connection to be able
to cache statements preparation, by means
+ * of appropriate pools.</p>
+ * <p>To use this class, you must add the following property to the example
configuration described in
+ * @link{org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader}
+ * </p>
+ * <pre><code>
+ * resource.loader.ds.database_objects_factory.class =
org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader<br>
+ * </code></pre>
+ * <p>The default size of each pool of prepared statements (there is one pool
per statement) is 50. You can tune it
+ * with:</p>
+ * <pre><code>
+ * resource.loader.ds.database_objects_factory. =
org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader<br>
+ * </code></pre>
+ * @see org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader
+ */
+
+public class CachingDatabaseObjectsFactory implements DatabaseObjectsFactory {
+
+ private static final String STATEMENTS_POOL_MAX_SIZE =
"statements_pool_max_size";
+ private static final int STATEMENTS_POOL_MAX_SIZE_DEFAULT = 50;
+
+ private DataSource dataSource;
+ private Connection connection;
+ private int poolsMaxSize;
+ private Map<String, GenericObjectPool<PreparedStatement>> statementsCache
= new HashMap<>();
+
+ private class PreparedStatementFactory extends
BasePooledObjectFactory<PreparedStatement>
+ {
+ private final String sql;
+
+ PreparedStatementFactory(String sql)
+ {
+ this.sql = sql;
+ }
+
+ @Override
+ public PreparedStatement create() throws Exception {
+ checkConnection();
+ return connection.prepareStatement(sql);
+ }
+
+ @Override
+ public PooledObject<PreparedStatement> wrap(PreparedStatement obj) {
+ return new DefaultPooledObject<>(obj);
+ }
+
+ @Override
+ public void destroyObject(final PooledObject<PreparedStatement> p)
throws Exception {
+ p.getObject().close();
+ }
+ }
+
+ /**
+ * Initialize the factory with the DataSourceResourceLoader properties
+ * @param dataSource data source
+ */
+ @Override
+ public void init(DataSource dataSource, ExtProperties properties) throws
SQLException
+ {
+ this.dataSource = dataSource;
+ this.connection = dataSource.getConnection();
+ this.poolsMaxSize =
Optional.ofNullable(properties.getInt(STATEMENTS_POOL_MAX_SIZE)).orElse(STATEMENTS_POOL_MAX_SIZE_DEFAULT);
+ }
+
+ /**
+ * Prepare a statement
+ * @param sql Statement SQL
+ * @return prepared statement
+ */
+ @Override
+ public synchronized PreparedStatement prepareStatement(String sql) throws
SQLException
+ {
+ GenericObjectPool<PreparedStatement> pool =
statementsCache.computeIfAbsent(sql, (String key) -> {
+ GenericObjectPoolConfig<PreparedStatement> poolConfig = new
GenericObjectPoolConfig<>();
+ poolConfig.setMaxTotal(poolsMaxSize);
+ return new GenericObjectPool<>(
+ new PreparedStatementFactory(sql),
+ poolConfig
+ );
+ });
+ try
+ {
+ return pool.borrowObject();
+ }
+ catch (SQLException sqle)
+ {
+ throw sqle;
+ }
+ catch (Exception e)
+ {
+ throw new SQLException("could not prepare statement", e);
+ }
+ }
+
+ private void checkConnection() throws SQLException
+ {
+ if (!connection.isValid(0))
+ {
+ // refresh connection
+ connection = dataSource.getConnection();
+ statementsCache = new HashMap<>();
+ }
+ }
+
+ /**
+ * Releases a prepared statement
+ * @param sql original sql query
+ * @param stmt statement
+ */
+ @Override
+ public void releaseStatement(String sql, PreparedStatement stmt) throws
SQLException
+ {
+ GenericObjectPool<PreparedStatement> pool = statementsCache.get(sql);
+ if (pool == null)
+ {
+ throw new SQLException("statement is not pooled");
+ }
+ pool.returnObject(stmt);
+ }
+
+ @Override
+ public void destroy()
+ {
+ statementsCache.values().forEach(pool ->
+ {
+ pool.close();
+ pool.clear();
+ });
+ try
+ {
+ connection.close();
+ }
+ catch (SQLException sqle) {}
+ };
+}
diff --git
a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java
index 9270023a..ecb827f6 100644
---
a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java
+++
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DataSourceResourceLoader.java
@@ -19,15 +19,11 @@ package org.apache.velocity.runtime.resource.loader;
* under the License.
*/
-import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-import org.apache.commons.pool2.impl.GenericObjectPool;
-import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.util.ClassUtils;
import org.apache.velocity.util.ExtProperties;
import org.apache.commons.lang3.StringUtils;
@@ -38,7 +34,6 @@ import javax.sql.DataSource;
import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;
-import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -126,6 +121,14 @@ import java.sql.Timestamp;
* template_timestamp datetime NOT NULL
* );
* </code></pre>
+ * <p>Prior to Velocity 2.4, this class should not be considered
thread-safe.</p>
+ * <p>Since Velocity 2.4, the handling of JDBC connections and prepared
statements is delegated to the
+ * {@link org.apache.velocity.runtime.resource.loader.DatabaseObjectsFactory}
instance. The default class for this
+ * database objets factory is {@link
org.apache.velocity.runtime.resource.loader.DefaultDatabaseObjectsFactory},
+ * which obtain a new connection from the data source and prepare statements
at each query. You can configure this
+ * resource loader to use the {@link
org.apache.velocity.runtime.resource.loader.CachingDatabaseObjectsFactory} which
+ * will keep a single connection and tries to reuse prepared statements.
+ * statements</p>
*
* @author <a href="mailto:[email protected]">Will Glass-Husain</a>
* @author <a href="mailto:[email protected]">Matt Raible</a>
@@ -138,61 +141,24 @@ import java.sql.Timestamp;
*/
public class DataSourceResourceLoader extends ResourceLoader
{
- private static final int STATEMENTS_POOL_MAX_SIZE_DEFAULT = 50;
- private String dataSourceName;
- private String tableName;
- private String keyColumn;
+ private static final String DATABASE_OBJECTS_FACTORY_DEFAULT_CLASS =
"org.apache.velocity.runtime.resource.loader.DefaultDatabaseObjectsFactory";
+
+ private DataSource dataSource;
+ private DatabaseObjectsFactory factory;
private String templateColumn;
private String timestampColumn;
- private InitialContext ctx;
- private DataSource dataSource;
-
- /*
- Keep connection open.
- */
- private Connection connection = null;
-
- /*
- Keep two pools for prepared statements
- */
- private GenericObjectPool<PreparedStatement> templatePrepStatementsPool;
- private GenericObjectPool<PreparedStatement> timestampPrepStatementsPool;
-
- private class PreparedStatementFactory extends
BasePooledObjectFactory<PreparedStatement>
- {
- private final String selectColumn;
-
- PreparedStatementFactory(String selectColumn)
- {
- this.selectColumn = selectColumn;
- }
-
- @Override
- public PreparedStatement create() throws Exception {
- return prepareStatement(connection, selectColumn, tableName,
keyColumn);
- }
+ private String templateSQL;
+ private String timestampSQL;
- @Override
- public PooledObject<PreparedStatement> wrap(PreparedStatement obj) {
- return new DefaultPooledObject<>(obj);
- }
-
- @Override
- public void destroyObject(final PooledObject<PreparedStatement> p)
throws Exception {
- p.getObject().close();
- }
- }
-
- private static class SelfCleaningReader extends FilterReader
+ private class SelfCleaningReader extends FilterReader
{
private ResultSet resultSet;
private ObjectPool<PreparedStatement> statementPool;
- public SelfCleaningReader(Reader reader, ResultSet resultSet,
ObjectPool<PreparedStatement> statementPool)
+ public SelfCleaningReader(Reader reader, ResultSet resultSet)
{
super(reader);
this.resultSet = resultSet;
- this.statementPool = statementPool;
}
@Override
@@ -202,7 +168,7 @@ public class DataSourceResourceLoader extends ResourceLoader
try
{
resultSet.close();
-
statementPool.returnObject((PreparedStatement)resultSet.getStatement());
+ factory.releaseStatement(templateSQL,
(PreparedStatement)resultSet.getStatement());
}
catch (RuntimeException re)
{
@@ -221,24 +187,36 @@ public class DataSourceResourceLoader extends
ResourceLoader
@Override
public void init(ExtProperties configuration)
{
- dataSourceName =
StringUtils.trim(configuration.getString("resource.datasource_url"));
- tableName =
StringUtils.trim(configuration.getString("resource.table"));
- keyColumn =
StringUtils.trim(configuration.getString("resource.key_column"));
+ String tableName =
StringUtils.trim(configuration.getString("resource.table"));
+ String keyColumn =
StringUtils.trim(configuration.getString("resource.key_column"));
templateColumn =
StringUtils.trim(configuration.getString("resource.template_column"));
timestampColumn =
StringUtils.trim(configuration.getString("resource.timestamp_column"));
+ templateSQL = "SELECT " + templateColumn + " FROM " + tableName + "
WHERE " + keyColumn + " = ?";
+ timestampSQL = "SELECT " + timestampColumn + " FROM " + tableName + "
WHERE " + keyColumn + " = ?";
+
+ String dataSourceName =
StringUtils.trim(configuration.getString("resource.datasource_url"));
+
if (dataSource != null)
{
log.debug("DataSourceResourceLoader: using dataSource instance
with table \"{}\"", tableName);
log.debug("DataSourceResourceLoader: using columns \"{}\", \"{}\"
and \"{}\"", keyColumn, templateColumn, timestampColumn);
- log.trace("DataSourceResourceLoader initialized.");
}
else if (dataSourceName != null)
{
log.debug("DataSourceResourceLoader: using \"{}\" datasource with
table \"{}\"", dataSourceName, tableName);
log.debug("DataSourceResourceLoader: using columns \"{}\", \"{}\"
and \"{}\"", keyColumn, templateColumn, timestampColumn);
+ try
+ {
+ dataSource = (DataSource) new
InitialContext().lookup(dataSourceName);
+ }
+ catch (NamingException ne)
+ {
+ throw new Error("could not lookup datasource for name: " +
dataSourceName, ne);
+ }
+
log.trace("DataSourceResourceLoader initialized.");
}
else
@@ -248,20 +226,23 @@ public class DataSourceResourceLoader extends
ResourceLoader
throw new RuntimeException(msg);
}
- /* initialize statements pools */
- int poolsMaxSize = configuration.getInt("statements_pool_max_size",
STATEMENTS_POOL_MAX_SIZE_DEFAULT);
- GenericObjectPoolConfig<PreparedStatement> poolConfig = new
GenericObjectPoolConfig<>();
- poolConfig.setMaxTotal(poolsMaxSize);
-
- templatePrepStatementsPool = new GenericObjectPool<>(
- new PreparedStatementFactory(templateColumn),
- poolConfig
- );
+ String factoryClassName =
configuration.getString("database_objects_factory.class");
+ if (factoryClassName == null)
+ {
+ factoryClassName = DATABASE_OBJECTS_FACTORY_DEFAULT_CLASS;
+ }
+ try
+ {
+ Class<?> factoryClass = ClassUtils.getClass(factoryClassName);
+ factory = (DatabaseObjectsFactory)
factoryClass.getDeclaredConstructor().newInstance();
+ factory.init(dataSource,
configuration.subset("database_objects_factory"));
+ }
+ catch (Exception e)
+ {
+ throw new Error("could not find database objects factory class",
e);
+ }
- timestampPrepStatementsPool = new GenericObjectPool<>(
- new PreparedStatementFactory(timestampColumn),
- poolConfig
- );
+ log.trace("DataSourceResourceLoader initialized.");
}
/**
@@ -271,6 +252,10 @@ public class DataSourceResourceLoader extends
ResourceLoader
*/
public void setDataSource(final DataSource dataSource)
{
+ if (factory != null)
+ {
+ throw new Error("cannot change data source after initialization");
+ }
this.dataSource = dataSource;
}
@@ -315,8 +300,7 @@ public class DataSourceResourceLoader extends ResourceLoader
ResultSet rs = null;
try
{
- checkDBConnection();
- PreparedStatement statement =
templatePrepStatementsPool.borrowObject();
+ PreparedStatement statement =
factory.prepareStatement(templateSQL);
rs = fetchResult(statement, name);
if (rs.next())
@@ -328,7 +312,7 @@ public class DataSourceResourceLoader extends ResourceLoader
+ "template column for '"
+ name + "' is null");
}
- return new SelfCleaningReader(reader, rs,
templatePrepStatementsPool);
+ return new SelfCleaningReader(reader, rs);
}
else
{
@@ -374,8 +358,7 @@ public class DataSourceResourceLoader extends ResourceLoader
ResultSet rs = null;
try
{
- checkDBConnection();
- statement = timestampPrepStatementsPool.borrowObject();
+ statement = factory.prepareStatement(timestampSQL);
rs = fetchResult(statement, name);
if (rs.next())
@@ -385,8 +368,7 @@ public class DataSourceResourceLoader extends ResourceLoader
}
else
{
- String msg = "DataSourceResourceLoader: could not find
resource "
- + name + " while " + operation;
+ String msg = "DataSourceResourceLoader: could not find
resource " + name + " while " + operation;
log.error(msg);
throw new ResourceNotFoundException(msg);
}
@@ -402,99 +384,23 @@ public class DataSourceResourceLoader extends
ResourceLoader
finally
{
closeResultSet(rs);
- if (statement != null) {
- timestampPrepStatementsPool.returnObject(statement);
+ if (statement != null)
+ {
+ try
+ {
+ factory.releaseStatement(timestampSQL, statement);
+ }
+ catch (SQLException sqle)
+ {
+ // just log, don't throw
+ log.error("DataSourceResourceLoader: error releasing
prepared statement", sqle);
+ }
}
}
}
return timeStamp;
}
- /**
- * Gets connection to the datasource specified through the configuration
- * parameters.
- *
- */
- private void openDBConnection() throws NamingException, SQLException
- {
- if (dataSource == null)
- {
- if (ctx == null)
- {
- ctx = new InitialContext();
- }
-
- dataSource = (DataSource) ctx.lookup(dataSourceName);
- }
-
- if (connection != null)
- {
- closeDBConnection();
- }
-
- connection = dataSource.getConnection();
- }
-
- /**
- * Checks the connection is valid
- *
- */
- private void checkDBConnection() throws NamingException, SQLException
- {
- if (connection == null || !connection.isValid(0))
- {
- openDBConnection();
- }
- }
-
- /**
- * Close DB connection on finalization
- *
- * @throws Throwable
- */
- @Override
- protected void finalize()
- throws Throwable
- {
- closeDBConnection();
- }
-
- /**
- * Closes the prepared statements and the connection to the datasource
- */
- private void closeDBConnection()
- {
- if (templatePrepStatementsPool != null)
- {
- templatePrepStatementsPool.close();
- templatePrepStatementsPool.clear();
- }
- if (timestampPrepStatementsPool != null)
- {
- timestampPrepStatementsPool.close();
- timestampPrepStatementsPool.clear();
- }
- if (connection != null)
- {
- try
- {
- connection.close();
- }
- catch (RuntimeException re)
- {
- throw re;
- }
- catch (SQLException e)
- {
- // ignore
- }
- finally
- {
- connection = null;
- }
- }
- }
-
/**
* Closes the result set.
*/
@@ -517,32 +423,6 @@ public class DataSourceResourceLoader extends
ResourceLoader
}
}
- /**
- * Creates the following PreparedStatement query :
- * <br>
- * SELECT <i>columnNames</i> FROM <i>tableName</i> WHERE <i>keyColumn</i>
- * = '<i>templateName</i>'
- * <br>
- * where <i>keyColumn</i> is a class member set in init()
- *
- * @param conn connection to datasource
- * @param columnNames columns to fetch from datasource
- * @param tableName table to fetch from
- * @param keyColumn column whose value should match templateName
- * @return PreparedStatement
- * @throws SQLException
- */
- protected PreparedStatement prepareStatement(
- final Connection conn,
- final String columnNames,
- final String tableName,
- final String keyColumn
- ) throws SQLException
- {
- PreparedStatement ps = conn.prepareStatement("SELECT " + columnNames +
" FROM "+ tableName + " WHERE " + keyColumn + " = ?");
- return ps;
- }
-
/**
* Fetches the result for a given template name.
* Inherit this method if there is any calculation to perform on the
template name.
@@ -562,17 +442,42 @@ public class DataSourceResourceLoader extends
ResourceLoader
}
/**
- * Gets a reader from a result set's column
- * @param resultSet
- * @param column
- * @param encoding
+ * Gets a reader from a result set's column.
+ * @param resultSet result set
+ * @param column template column
+ * @param encoding template encoding (unused)
* @return reader
* @throws SQLException
+ * @deprecated the 'encoding' parameter is useless, it should have been
set in the database. Use {@link #getReader(ResultSet, String)}
*/
protected Reader getReader(ResultSet resultSet, String column, String
encoding)
throws SQLException
+ {
+ return getReader(resultSet, column);
+ }
+
+ /**
+ * Gets a reader from a result set's column.
+ * @param resultSet result set
+ * @param column template column
+ * @return reader
+ * @throws SQLException
+ */
+ protected Reader getReader(ResultSet resultSet, String column)
+ throws SQLException
{
return resultSet.getCharacterStream(column);
}
+ /**
+ * Frees all resources.
+ */
+ public void destroy()
+ {
+ if (factory != null)
+ {
+ factory.destroy();
+ }
+ }
+
}
diff --git
a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DatabaseObjectsFactory.java
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DatabaseObjectsFactory.java
new file mode 100644
index 00000000..34c9d8dc
--- /dev/null
+++
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DatabaseObjectsFactory.java
@@ -0,0 +1,39 @@
+package org.apache.velocity.runtime.resource.loader;
+
+import org.apache.velocity.util.ExtProperties;
+
+import javax.sql.DataSource;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Factory for creating connections and prepared statements
+ */
+
+public interface DatabaseObjectsFactory {
+
+ /**
+ * Initialize the factory with the DataSourceResourceLoader properties
+ * @param dataSource data source
+ */
+ void init(DataSource dataSource, ExtProperties properties) throws
SQLException;
+
+ /**
+ * Prepare a statement
+ * @param sql Statement SQL
+ * @return prepared statement
+ */
+ PreparedStatement prepareStatement(String sql) throws SQLException;
+
+ /**
+ * Releases a prepared statement
+ * @param sql original sql query
+ * @param stmt statement
+ */
+ void releaseStatement(String sql, PreparedStatement stmt) throws
SQLException;
+
+ /**
+ * Free resources
+ */
+ default void destroy() {};
+}
diff --git
a/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DefaultDatabaseObjectsFactory.java
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DefaultDatabaseObjectsFactory.java
new file mode 100644
index 00000000..ed52d5a2
--- /dev/null
+++
b/velocity-engine-core/src/main/java/org/apache/velocity/runtime/resource/loader/DefaultDatabaseObjectsFactory.java
@@ -0,0 +1,53 @@
+package org.apache.velocity.runtime.resource.loader;
+
+import org.apache.velocity.util.ExtProperties;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Database objects factory which will obtain a new connection from the data
source and prepare needed statements
+ * at each call
+ */
+
+public class DefaultDatabaseObjectsFactory implements DatabaseObjectsFactory {
+
+ private DataSource dataSource;
+
+ /**
+ * Initialize the factory with the DataSourceResourceLoader properties
+ * @param dataSource data source
+ */
+ @Override
+ public void init(DataSource dataSource, ExtProperties properties)
+ {
+ this.dataSource = dataSource;
+ }
+
+ /**
+ * Prepare a statement
+ * @param sql Statement SQL
+ * @return prepared statement
+ */
+ @Override
+ public PreparedStatement prepareStatement(String sql) throws SQLException
+ {
+ Connection connection = dataSource.getConnection();
+ return connection.prepareStatement(sql);
+ }
+
+ /**
+ * Releases a prepared statement
+ * @param sql original sql query
+ * @param stmt statement
+ */
+ @Override
+ public void releaseStatement(String sql, PreparedStatement stmt) throws
SQLException
+ {
+ Connection connection = stmt.getConnection();
+ stmt.close();
+ connection.close();
+ }
+}
diff --git
a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java
b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java
index 8e4caa4d..d4333b73 100644
---
a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java
+++
b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java
@@ -98,14 +98,9 @@ public class DataSourceResourceLoaderTestCase
DataSourceResourceLoader rl2 = new DataSourceResourceLoader();
rl2.setDataSource(ds2);
- ExtProperties props = new ExtProperties();
- props.addProperty( "resource.loader", "ds" );
+ ExtProperties props = getResourceLoaderProperties();
props.setProperty( "ds.resource.loader.instance", rl1);
- props.setProperty( "ds.resource.loader.resource.table",
"velocity_template_varchar");
- props.setProperty( "ds.resource.loader.resource.keycolumn",
"vt_id");
- props.setProperty( "ds.resource.loader.resource.templatecolumn",
"vt_def");
- props.setProperty( "ds.resource.loader.resource.timestampcolumn",
"vt_timestamp");
- props.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger(false,
false));
+ props.setProperty( "ds.resource.loader.resource.table",
"velocity_template_varchar");
varcharTemplatesEngine = new RuntimeInstance();
varcharTemplatesEngine.setConfiguration(props);
@@ -113,12 +108,23 @@ public class DataSourceResourceLoaderTestCase
ExtProperties props2 = (ExtProperties)props.clone();
props2.setProperty( "ds.resource.loader.instance", rl2);
- props2.setProperty( "ds.resource.loader.resource.table",
"velocity_template_clob");
+ props2.setProperty( "ds.resource.loader.resource.table",
"velocity_template_clob");
clobTemplatesEngine = new RuntimeInstance();
clobTemplatesEngine.setConfiguration(props2);
clobTemplatesEngine.init();
}
+ protected ExtProperties getResourceLoaderProperties()
+ {
+ ExtProperties props = new ExtProperties();
+ props.addProperty( "resource.loader", "ds" );
+ props.setProperty( "ds.resource.loader.resource.keycolumn",
"vt_id");
+ props.setProperty( "ds.resource.loader.resource.templatecolumn",
"vt_def");
+ props.setProperty( "ds.resource.loader.resource.timestampcolumn",
"vt_timestamp");
+ props.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger(false,
false));
+ return props;
+ }
+
/**
* Tests loading and rendering of a simple template. If that works, we are
able to get data
* from the database.
diff --git
a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DatasourceResourceLoaderCachingFactoryTestCase.java
b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DatasourceResourceLoaderCachingFactoryTestCase.java
new file mode 100644
index 00000000..7f5e487b
--- /dev/null
+++
b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DatasourceResourceLoaderCachingFactoryTestCase.java
@@ -0,0 +1,19 @@
+package org.apache.velocity.test.sql;
+
+import org.apache.velocity.util.ExtProperties;
+
+public class DatasourceResourceLoaderCachingFactoryTestCase extends
DataSourceResourceLoaderTestCase
+{
+ public DatasourceResourceLoaderCachingFactoryTestCase(String name) throws
Exception {
+ super(name);
+ }
+
+ @Override
+ protected ExtProperties getResourceLoaderProperties()
+ {
+ ExtProperties props = super.getResourceLoaderProperties();
+ props.put("ds.database_objects_factory.class",
"org.apache.velocity.resource.loader.CachingDatabaseObjectsFactory");
+ return props;
+ }
+
+}