You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2019/04/18 14:34:54 UTC

svn commit: r1857751 - in /velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model: sql/ util/

Author: cbrisson
Date: Thu Apr 18 14:34:54 2019
New Revision: 1857751

URL: http://svn.apache.org/viewvc?rev=1857751&view=rev
Log:
[tools/model] Initial import: sql utilities

Added:
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/BasicDataSource.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionPool.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionWrapper.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Credentials.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/DriverInfos.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pool.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pooled.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/PooledStatement.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/RowValues.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/SqlUtils.java
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/StatementPool.java
Modified:
    velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/util/TypeUtils.java

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/BasicDataSource.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/BasicDataSource.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/BasicDataSource.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/BasicDataSource.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,74 @@
+package org.apache.velocity.tools.model.sql;
+
+import javax.sql.DataSource;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+
+public class BasicDataSource implements DataSource
+{
+    public BasicDataSource(String databaseURL)
+    {
+        this.databaseURL = databaseURL;
+    }
+
+    @Override
+    public Connection getConnection() throws SQLException
+    {
+        return DriverManager.getConnection(databaseURL);
+    }
+
+    @Override
+    public Connection getConnection(String user, String password) throws SQLException
+    {
+        return DriverManager.getConnection(databaseURL, user, password);
+    }
+
+    @Override
+    public <T> T unwrap(Class<T> iface) throws SQLException
+    {
+        return null;
+    }
+
+    @Override
+    public boolean isWrapperFor(Class<?> iface) throws SQLException
+    {
+        return false;
+    }
+
+    @Override
+    public PrintWriter getLogWriter() throws SQLException
+    {
+        return logWriter;
+    }
+
+    @Override
+    public void setLogWriter(PrintWriter out) throws SQLException
+    {
+        logWriter = out;
+    }
+
+    @Override
+    public void setLoginTimeout(int seconds) throws SQLException
+    {
+
+    }
+
+    @Override
+    public int getLoginTimeout() throws SQLException
+    {
+        return 0;
+    }
+
+    @Override
+    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
+    {
+        throw new SQLFeatureNotSupportedException("not using JDK logging");
+    }
+
+    private String databaseURL;
+
+    private PrintWriter logWriter = null;
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionPool.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionPool.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionPool.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionPool.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,199 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sql.DataSource;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *  Connection pool.
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class ConnectionPool implements Serializable
+{
+    protected static Logger logger = LoggerFactory.getLogger(ConnectionPool.class);
+
+    /**
+     * Constructor.
+     * @param schema schema
+     * @param driverInfos infos on the driverInfos
+     * @param autocommit autocommit
+     * @param max max connections
+     * @throws SQLException
+     * /
+    public ConnectionPool(String schema, DriverInfos driverInfos, boolean autocommit, int max)
+            throws SQLException
+    {
+        this.schema = schema;
+        this.driverInfos = driverInfos;
+        this.autocommit = autocommit;
+        connections = new ArrayList<ConnectionWrapper>();
+        this.max = max;
+    }
+    */
+
+    /**
+     *
+     * @param dataSource
+     * @param schema
+     * @param max
+     * @throws SQLException
+     */
+    public ConnectionPool(DataSource dataSource, Credentials credentials, DriverInfos driverInfos, String schema, boolean autocommit, int max) throws SQLException
+    {
+        this.dataSource = dataSource;
+        this.credentials = credentials;
+        this.driverInfos = driverInfos;
+        this.schema = schema;
+        this.autocommit = autocommit;
+        this.max = max;
+    }
+
+    public DataSource getDataSource()
+    {
+        return dataSource;
+    }
+
+    public void setSchema(String schema)
+    {
+        this.schema = schema;
+    }
+
+    /**
+     * Get a connection.
+     * @return a connection
+     * @throws SQLException
+     */
+    public synchronized ConnectionWrapper getConnection() throws SQLException
+    {
+        for(Iterator it = connections.iterator(); it.hasNext(); )
+        {
+            ConnectionWrapper c = (ConnectionWrapper)it.next();
+
+            if(c.isClosed())
+            {
+                it.remove();
+            }
+            else if(!c.isBusy())
+            {
+                return c;
+            }
+        }
+        if(connections.size() == max)
+        {
+            logger.warn("Connection pool: max number of connections reached! ");
+
+            // return a busy connection...
+            return connections.get(0);
+        }
+
+        ConnectionWrapper newconn = createConnection();
+
+        connections.add(newconn);
+        return newconn;
+    }
+
+    /**
+     * Create a connection.
+     *
+     * @return connection
+     * @throws SQLException
+     */
+    private ConnectionWrapper createConnection() throws SQLException
+    {
+        logger.info("Creating a new connection.");
+
+        Connection connection = credentials.getConnection(dataSource);
+
+        // schema
+        if(schema != null && schema.length() > 0)
+        {
+            String schemaQuery = driverInfos.getSchemaQuery();
+
+            if(schemaQuery != null)
+            {
+                schemaQuery = schemaQuery.replace("$schema", schema);
+                Statement stmt = connection.createStatement();
+
+                stmt.executeUpdate(schemaQuery);
+                stmt.close();
+            }
+        }
+
+        // autocommit
+        connection.setAutoCommit(autocommit);
+        return new ConnectionWrapper(driverInfos, connection);
+    }
+
+/*
+    private String getSchema(Connection connection) throws SQLException
+    {
+        Statement stmt = connection.createStatement();
+        ResultSet rs = stmt.executeQuery("select sys_context('userenv','current_schema') from dual");
+        rs.next();
+        return rs.getString(1);
+    }*/
+
+    /**
+     * clear all connections.
+     */
+    public void clear()
+    {
+        for(Iterator it = connections.iterator(); it.hasNext(); )
+        {
+            ConnectionWrapper c = (ConnectionWrapper)it.next();
+
+            try
+            {
+                c.close();
+            }
+            catch(SQLException sqle) {}
+        }
+    }
+
+    private DataSource dataSource;
+
+    private Credentials credentials;
+
+    /** optional schema */
+    private String schema = null;
+
+    /** infos on the driverInfos */
+    private DriverInfos driverInfos = null;
+
+    /** autocommit flag */
+    private boolean autocommit = true;
+
+    /** list of all connections */
+    private List<ConnectionWrapper> connections = new ArrayList<>();
+
+    /** Maximum number of connections. */
+    private int max;
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionWrapper.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionWrapper.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionWrapper.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/ConnectionWrapper.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,1029 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+/**
+ * Connection wrapper class. Allows the handling of a busy state
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+
+public class ConnectionWrapper
+    implements Connection, Serializable
+{
+
+    protected static Logger logger = LoggerFactory.getLogger(ConnectionWrapper.class);
+
+    /**
+     * Constructor.
+     * @param driverInfos infos on the driver
+     * @param connection connection to be wrapped
+     */
+    public ConnectionWrapper(DriverInfos driverInfos, Connection connection)
+    {
+        this.driverInfos = driverInfos;
+        this.connection = connection;
+    }
+
+    /**
+     * Unwrap the connection.
+     * @return the unwrapped connection
+     */
+    public Connection unwrap()
+    {
+        return connection;
+    }
+
+    /**
+     * Create a statement.
+     * @return created statement
+     * @throws SQLException
+     */
+    public synchronized Statement createStatement()
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createStatement();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a statement.
+     * @param s SQL query
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized PreparedStatement prepareStatement(String s)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareStatement(s);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a callable statement.
+     * @param s SQL query
+     * @return prepared callable statement
+     * @throws SQLException
+     */
+    public synchronized CallableStatement prepareCall(String s)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareCall(s);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Gets native SQL for a query.
+     * @param s query
+     * @return native SQL
+     * @throws SQLException
+     */
+    public synchronized String nativeSQL(String s)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.nativeSQL(s);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Set autocommit flag.
+     * @param flag autocommit
+     * @throws SQLException
+     */
+    public void setAutoCommit(boolean flag)
+        throws SQLException
+    {
+        connection.setAutoCommit(flag);
+    }
+
+    /**
+     * Get autocommit flag.
+     *
+     * @return autocommit flag
+     * @throws SQLException
+     */
+    public boolean getAutoCommit()
+        throws SQLException
+    {
+        return connection.getAutoCommit();
+    }
+
+    /**
+     * Commit.
+     *
+     * @throws SQLException
+     */
+    public synchronized void commit()
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            connection.commit();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Rollback.
+     *
+     * @throws SQLException
+     */
+    public synchronized void rollback()
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            connection.rollback();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Close.
+     *
+     * @throws SQLException
+     */
+    public void close()
+        throws SQLException
+    {
+        // since some sql drivers refuse to close a connection that has been interrupted,
+        // better handle this also ourselves
+        closed = true;
+        connection.close();
+    }
+
+    /**
+     * Check the closed state.
+     * @return closed state
+     * @throws SQLException
+     */
+    public boolean isClosed()
+        throws SQLException
+    {
+        return (closed || connection == null || connection.isClosed());
+    }
+
+    /**
+     * Get meta data
+     * @return database meta data
+     * @throws SQLException
+     */
+    public DatabaseMetaData getMetaData()
+        throws SQLException
+    {
+        return connection.getMetaData();
+    }
+
+    /**
+     * set read-only flag
+     * @param flag read-only
+     * @throws SQLException
+     */
+    public void setReadOnly(boolean flag)
+        throws SQLException
+    {
+        connection.setReadOnly(flag);
+    }
+
+    /**
+     * Check the read-only state.
+     * @return read-only state
+     * @throws SQLException
+     */
+    public boolean isReadOnly()
+        throws SQLException
+    {
+        return connection.isReadOnly();
+    }
+
+    /**
+     * Catalog setter.
+     * @param s catalog
+     * @throws SQLException
+     */
+    public void setCatalog(String s)
+        throws SQLException
+    {
+        connection.setCatalog(s);
+    }
+
+    /**
+     * Catalog getter.
+     * @return catalog
+     * @throws SQLException
+     */
+
+    public String getCatalog()
+        throws SQLException
+    {
+        return connection.getCatalog();
+    }
+    /**
+     * Transaction isolation setter.
+     * @param i transaction isolation
+     * @throws SQLException
+     */
+
+    public void setTransactionIsolation(int i)
+        throws SQLException
+    {
+        connection.setTransactionIsolation(i);
+    }
+
+    /**
+     * Transaction isolation getter.
+     * @return transaction isolation
+     * @throws SQLException
+     */
+    public int getTransactionIsolation()
+        throws SQLException
+    {
+        return connection.getTransactionIsolation();
+    }
+
+    /**
+     * Get SQL warnings.
+     * @return next SQL Warning.
+     * @throws SQLException
+     */
+    public SQLWarning getWarnings()
+        throws SQLException
+    {
+        return connection.getWarnings();
+    }
+
+    /**
+     * Clear SQL warnings.
+     * @throws SQLException
+     */
+    public void clearWarnings()
+        throws SQLException
+    {
+        connection.clearWarnings();
+    }
+
+    /**
+     * Create a statement.
+     *
+     * @param i result set type
+     * @param j result set concurrency
+     * @return new statement
+     * @throws SQLException
+     */
+    public synchronized Statement createStatement(int i, int j)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createStatement(i, j);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+
+    }
+
+    /**
+     * Prepare a statement.
+     * @param s SQL query
+     * @param i result set type
+     * @param j result set concurrency
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized PreparedStatement prepareStatement(String s, int i, int j)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareStatement(s, i, j);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a call.
+     * @param s SQL query
+     * @param i result set type
+     * @param j result set concurrency
+     * @return callable statement
+     * @throws SQLException
+     */
+    public synchronized CallableStatement prepareCall(String s, int i, int j)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareCall(s, i, j);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Get type map.
+     * @return type map
+     * @throws SQLException
+     */
+    public Map getTypeMap()
+        throws SQLException
+    {
+        return connection.getTypeMap();
+    }
+
+    /**
+     * Set type map.
+     * @param map type map
+     * @throws SQLException
+     */
+    public void setTypeMap(Map map)
+        throws SQLException
+    {
+        connection.setTypeMap(map);
+    }
+
+    /**
+     * Set holdability.
+     * @param i holdability
+     * @throws SQLException
+     */
+    public void setHoldability(int i)
+        throws SQLException
+    {
+        connection.setHoldability(i);
+    }
+
+    /**
+     * Get holdability.
+     * @return holdability
+     * @throws SQLException
+     */
+    public int getHoldability()
+        throws SQLException
+    {
+        return connection.getHoldability();
+    }
+
+    /**
+     * Savepoint setter.
+     * @return save point
+     * @throws SQLException
+     */
+    public synchronized Savepoint setSavepoint()
+        throws SQLException
+    {
+        return connection.setSavepoint();
+    }
+
+    /**
+     * Set named savepoint.
+     * @param s savepoint name
+     * @return savepoint
+     * @throws SQLException
+     */
+    public synchronized Savepoint setSavepoint(String s)
+        throws SQLException
+    {
+        return connection.setSavepoint(s);
+    }
+
+    /**
+     * Rollback.
+     * @param savepoint savepoint
+     * @throws SQLException
+     */
+    public synchronized void rollback(Savepoint savepoint)
+        throws SQLException
+    {
+        connection.rollback(savepoint);
+    }
+    /**
+     * Release savepoint.
+     *
+     * @param savepoint savepoint
+     * @throws SQLException
+     */
+    public synchronized void releaseSavepoint(Savepoint savepoint)
+        throws SQLException
+    {
+        connection.releaseSavepoint(savepoint);
+    }
+
+    /**
+     * Create a statement.
+     * @param i result set type
+     * @param j result set concurrency
+     * @param k result set holdability
+     * @return created statement
+     * @throws SQLException
+     */
+    public synchronized Statement createStatement(int i, int j, int k)
+        throws SQLException
+    {
+        try
+        {
+           enterBusyState();
+            return connection.createStatement(i, j, k);
+        }
+        finally 
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a statement.
+     * @param s SQL query
+     * @param i result set type
+     * @param j result set concurrency
+     * @param k result set holdability
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized PreparedStatement prepareStatement(String s, int i, int j, int k)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareStatement(s, i, j, k);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a callable statement.
+     * @param s SQL query
+     * @param i result set type
+     * @param j result set concurrency
+     * @param k result set holdability
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized CallableStatement prepareCall(String s, int i, int j, int k)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareCall(s, i, j, k);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a statement.
+     * @param s SQL query
+     * @param i autogenerated keys
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized PreparedStatement prepareStatement(String s, int i)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareStatement(s, i);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a statement.
+     * @param s SQL query
+     * @param ai autogenerated keys column indexes
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized PreparedStatement prepareStatement(String s, int[] ai)
+        throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.prepareStatement(s, ai);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /**
+     * Prepare a statement.
+     * @param s SQL query
+     * @param as autogenerated keys column names
+     * @return prepared statement
+     * @throws SQLException
+     */
+    public synchronized PreparedStatement prepareStatement(String s, String[] as)
+        throws SQLException
+    {
+        try     
+        {
+            enterBusyState();
+            return connection.prepareStatement(s,as);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    /** 
+     * Enter busy state.
+     */
+    public synchronized void enterBusyState()
+    {
+        //Logger.trace("connection #"+toString()+": entering busy state.");
+        busy++;
+    }
+
+    /**
+     * Leave busy state.
+     */
+    public synchronized void leaveBusyState()
+    {
+        lastUse = System.currentTimeMillis();
+        busy--;
+        //Logger.trace("connection #"+toString()+": leaving busy state.");
+    }
+
+    /**
+     * Check busy state.
+     * @return busy state
+     */
+    public boolean isBusy()
+    {
+        return busy > 0;
+    }
+
+    /**
+     * Get last use timestamp
+     *
+     * @return last use
+     */
+    public long getLastUse()
+    {
+      return lastUse;
+    }
+
+    public DriverInfos getDriverInfos()
+    {
+        return driverInfos;
+    }
+
+    /**
+     * Get last inserted ID.
+     *
+     * @param statement
+     * @return last inserted id
+     * @throws SQLException
+     */
+    public long getLastInsertId(Statement statement, String keyColumn) throws SQLException
+    {
+        long ret = -1;
+        switch (driverInfos.getLastInsertIdPolicy())
+        {
+            case METHOD:
+            {
+                if (lastInsertIdMethod == null)
+                {
+                    synchronized(this)
+                    {
+                        if (lastInsertIdMethod == null)
+                        {
+                            try
+                            {
+                                lastInsertIdMethod = statement.getClass().getMethod(driverInfos.getLastInsertIdMethodName());
+                            }
+                            catch (NoSuchMethodException nsme)
+                            {
+                                throw new SQLException("cannot get last insert id", nsme);
+                            }
+                        }
+                    }
+                }
+                try
+                {
+                    ret = ((Long)lastInsertIdMethod.invoke(statement)).longValue();
+                }
+                catch (IllegalAccessException | InvocationTargetException e)
+                {
+                    throw new SQLException("cannot get last insert id", e);
+                }
+                break;
+            }
+            case GENERATED_KEYS:
+            {
+                ResultSet rs = statement.getGeneratedKeys();
+                ResultSetMetaData meta = rs.getMetaData();
+                int colNum = meta.getColumnCount();
+                if (rs.next())
+                {
+                    ret = colNum == 1 ? rs.getLong(1) : rs.getLong(keyColumn);
+                    if (rs.wasNull())
+                    {
+                        ret = -1;
+                    }
+                }
+                rs.close();
+            }
+            case QUERY:
+            {
+                ResultSet rs = statement.getConnection().createStatement().executeQuery(driverInfos.getLastInsertIdQuery());
+                rs.next();
+                ret = rs.getLong(1);
+                if (rs.wasNull())
+                {
+                    ret = -1;
+                }
+                rs.close();
+            }
+            case RETURNING:
+            {
+                throw new SQLException("not implemented");
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Check connection.
+     *
+     * @return true if the connection is ok
+     */
+    public synchronized boolean check()
+    {
+        try
+        {
+            String checkQuery = driverInfos.getPingQuery();
+            if (checkQuery == null)
+            {
+                // at least, call isClosed
+                return !isClosed();
+            }
+            else
+            {
+                if (checkStatement == null)
+                {
+                    checkStatement = prepareStatement(checkQuery);
+                }
+                checkStatement.executeQuery();
+            }
+            return true;
+        }
+        catch (Exception e)
+        {
+            logger.warn("Exception while checking connection. Refreshing...");
+            return false;
+        }
+    }
+
+    /** Infos on the driver. */
+    private DriverInfos driverInfos = null;
+
+    private Method lastInsertIdMethod = null;
+
+    /** Wrapped connection. */
+    private transient Connection connection = null;
+
+    /** Busy state. */
+    private int busy = 0;
+
+    /** Last use */
+    private long lastUse = System.currentTimeMillis();
+
+    /** Closed state. */
+    private boolean closed = false;
+
+    /** statement used to check connection ("select 1").
+     */
+    private transient PreparedStatement checkStatement = null;
+
+    /*
+     * 1.6 API
+     */
+
+    public Clob createClob() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createClob();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+		}
+
+    public Blob createBlob() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createBlob();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public NClob createNClob() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createNClob();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public SQLXML createSQLXML() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createSQLXML();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public boolean isValid(int timeout) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.isValid(timeout);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public void setClientInfo(String name,String value) throws SQLClientInfoException
+    {
+        try
+        {
+            enterBusyState();
+            connection.setClientInfo(name, value);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public void setClientInfo(Properties properties) throws SQLClientInfoException
+    {
+        try
+        {
+            enterBusyState();
+            connection.setClientInfo(properties);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public Properties getClientInfo() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.getClientInfo();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public String getClientInfo(String name) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.getClientInfo(name);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public Array createArrayOf(String typeName, Object[] elements) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createArrayOf(typeName, elements);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public Struct createStruct(String typeName, Object[] attributes) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.createStruct(typeName, attributes);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public boolean isWrapperFor(Class<?> iface) throws SQLException
+    {
+        throw new SQLException("Unsupported method.");
+    }
+
+    public <T> T unwrap(Class<T> iface) throws SQLException
+    {
+        throw new SQLException("Unsupported method.");
+    }
+
+		/*
+		 * 1.7 API
+	 	*/
+
+    public void setSchema(String schema) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            connection.setSchema(schema);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public String getSchema() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.getSchema();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public void abort(Executor executor) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            connection.abort(executor);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            connection.setNetworkTimeout(executor, milliseconds);
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+
+    public int getNetworkTimeout() throws SQLException
+    {
+        try
+        {
+            enterBusyState();
+            return connection.getNetworkTimeout();
+        }
+        finally
+        {
+            leaveBusyState();
+        }
+    }
+		
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Credentials.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Credentials.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Credentials.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Credentials.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,45 @@
+package org.apache.velocity.tools.model.sql;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public final class Credentials
+{
+    public Credentials() {}
+
+    public String getUser()
+    {
+        return user;
+    }
+
+    public boolean hasCredentials()
+    {
+        return user != null && password != null;
+    }
+
+    public void setUser(String user)
+    {
+        this.user = user;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public Connection getConnection(DataSource dataSource) throws SQLException
+    {
+        if (hasCredentials())
+        {
+            return dataSource.getConnection(user, password);
+        }
+        else
+        {
+            return dataSource.getConnection();
+        }
+    }
+
+    private String user = null;
+    private String password = null;
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/DriverInfos.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/DriverInfos.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/DriverInfos.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/DriverInfos.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,381 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.velocity.tools.config.ConfigurationException;
+import org.apache.velocity.tools.model.config.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.function.UnaryOperator;
+import java.util.regex.Pattern;
+
+/**
+ * <p>Contains specific description and behaviour of jdbc drivers.</p>
+ *
+ * <p>Main sources:
+ * <ul><li>http://www.schemaresearch.com/products/srtransport/doc/modules/jdbcconf.html
+ * <li>http://db.apache.org/torque/ and org.apache.torque.adapter classes
+ * </ul></p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class DriverInfos implements Constants, Serializable
+{
+    protected static Logger logger = LoggerFactory.getLogger(DriverInfos.class);
+
+    /*
+     * Constructors
+     */
+
+    public DriverInfos()
+    {
+    }
+
+    public void setDefaults(DriverInfos other)
+    {
+        setTag(Optional.ofNullable(getTag()).orElse(other.getTag()));
+        setCatalog(Optional.ofNullable(getCatalog()).orElse(other.getCatalog()));
+        setPingQuery(Optional.ofNullable(getPingQuery()).orElse(other.getPingQuery()));
+        setTablesCaseSensitivity(Optional.ofNullable(getTablesCaseSensitivity()).orElse(other.getTablesCaseSensitivity()));
+        setSchemaQuery(Optional.ofNullable(getSchemaQuery()).orElse(other.getSchemaQuery()));
+        setLastInsertIdPolicy(Optional.ofNullable(getLastInsertIdPolicyString()).orElse(Optional.ofNullable(other.getLastInsertIdPolicyString()).orElse("none")));
+        setPedanticColumnTypes(Optional.ofNullable(isPedanticColumnTypes()).orElse(Optional.ofNullable(other.isPedanticColumnTypes()).orElse(false)));
+        setColumnMarkers(Optional.ofNullable(hasColumnMarkers()).orElse(Optional.ofNullable(other.hasColumnMarkers()).orElse(false)));
+        Pattern ignoreTablesPattern = Optional.ofNullable(getIgnoreTablesPattern()).orElse(other.getIgnoreTablesPattern());
+        setIgnoreTablesPattern(ignoreTablesPattern == null ? null : ignoreTablesPattern.toString());
+        Character idQuoteChar = Optional.of(getIdentifierQuoteChar()).orElse(other.getIdentifierQuoteChar());
+        setIdentifierQuoteChar(idQuoteChar == null ? null : String.valueOf(idQuoteChar));
+    }
+
+    /*
+     * Getters and setters
+     */
+
+    public String getTag()
+    {
+        return tag;
+    }
+
+    public void setTag(String tag)
+    {
+        this.tag = tag;
+    }
+
+    public String getCatalog()
+    {
+        return catalog;
+    }
+
+    public void setCatalog(String catalog)
+    {
+        this.catalog = catalog;
+    }
+
+    public String getPingQuery()
+    {
+        return pingQuery;
+    }
+
+    public void setPingQuery(String query)
+    {
+        this.pingQuery = pingQuery;
+    }
+
+    public CaseSensitivity getTablesCaseSensitivity()
+    {
+        return tablesCaseSensitivity;
+    }
+
+    public void setTablesCaseSensitivity(CaseSensitivity tablesCaseSensitivity)
+    {
+        this.tablesCaseSensitivity = tablesCaseSensitivity;
+        switch (tablesCaseSensitivity)
+        {
+            case LOWERCASE: filterTableName = t -> t.toLowerCase(Locale.ROOT); break;
+            case UPPERCASE: filterTableName = t -> t.toUpperCase(Locale.ROOT); break;
+            default: filterTableName = t -> t;
+        }
+    }
+
+    public String getSchemaQuery()
+    {
+        return schemaQuery;
+    }
+
+    public void setSchemaQuery(String schemaQuery)
+    {
+        this.schemaQuery = schemaQuery;
+    }
+
+    public LastInsertIdPolicy getLastInsertIdPolicy()
+    {
+        return lastInsertIdPolicy;
+    }
+
+    public String getLastInsertIdPolicyString()
+    {
+        if (lastInsertIdPolicy == null)
+        {
+            return null;
+        }
+        switch (lastInsertIdPolicy)
+        {
+            case NONE: return "none";
+            case GENERATED_KEYS: return "generated_keys";
+            case METHOD: return "method:" + getLastInsertIdMethodName();
+            case QUERY: return "query:" + getLastInsertIdQuery();
+            default: return null;
+        }
+    }
+
+    public void setLastInsertIdPolicy(String policy)
+    {
+        if (policy.startsWith("query:"))
+        {
+            lastInsertIdPolicy = LastInsertIdPolicy.QUERY;
+            lastInsertIdQuery = policy.substring(6).trim();
+        }
+        else if (policy.startsWith("method:"))
+        {
+            lastInsertIdPolicy = LastInsertIdPolicy.METHOD;
+            lastInsertIdMethodName = policy.substring(7).trim();
+        }
+        else
+        {
+            lastInsertIdPolicy = LastInsertIdPolicy.valueOf(policy.toUpperCase());
+            if (lastInsertIdPolicy == LastInsertIdPolicy.RETURNING)
+            {
+                throw new ConfigurationException("'returning' last insert id policy is not yet supported");
+            }
+        }
+    }
+    
+    public String getLastInsertIdQuery()
+    {
+        return lastInsertIdQuery;
+    }
+
+    public String getLastInsertIdMethodName()
+    {
+        return lastInsertIdMethodName;
+    }
+
+    public Boolean isPedanticColumnTypes()
+    {
+        return pedanticColumnTypes;
+    }
+
+    public void setPedanticColumnTypes(boolean pedanticColumnTypes)
+    {
+        this.pedanticColumnTypes = pedanticColumnTypes;
+    }
+
+    public Pattern getIgnoreTablesPattern()
+    {
+        return ignoreTablesPattern;
+    }
+
+    public void setIgnoreTablesPattern(String pattern)
+    {
+        if (pattern != null && pattern.length() > 0)
+        {
+            ignoreTablesPattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+        }
+        else
+        {
+            ignoreTablesPattern = null;
+        }
+    }
+
+    public Character getIdentifierQuoteChar()
+    {
+        return identifierQuoteChar;
+    }
+
+    public void setIdentifierQuoteChar(String identifierQuoteChar)
+    {
+        identifierQuoteChar = identifierQuoteChar.trim();
+        if (identifierQuoteChar.length() > 0)
+        {
+            this.identifierQuoteChar = identifierQuoteChar.charAt(0);
+        }
+    }
+
+    public Boolean hasColumnMarkers()
+    {
+        return columnMarkers;
+    }
+
+    public void setColumnMarkers(boolean columnMarkers)
+    {
+        this.columnMarkers = columnMarkers;
+    }
+
+    /*
+     * Operations
+     */
+
+    public String getTableName(String entityName)
+    {
+        return filterTableName.apply(entityName);
+    }
+
+    public String quoteIdentifier(String id)
+    {
+        if (identifierQuoteChar == ' ')
+        {
+            return id;
+        }
+        else
+        {
+            return identifierQuoteChar + id + identifierQuoteChar;
+        }
+    }
+
+    /**
+     * Check whether to ignore or not this table.
+     *
+     * @param name table name
+     * @return whether to ignore this table
+     */
+    public boolean ignoreTable(String name)
+    {
+        return ignoreTablesPattern != null && ignoreTablesPattern.matcher(name).matches();
+    }
+
+    /**
+     * Driver-specific value filtering
+     *
+     * @param value value to be filtered
+     * @return filtered value
+     */
+    public Object filterValue(Object value)
+    {
+        if(value instanceof Calendar && "mysql".equals(tag))
+        {
+            value = ((Calendar)value).getTime();
+        }
+        return value;
+    }
+
+    /*
+     * Members
+     */
+
+    /** jdbc tag of the database vendor */
+    private String tag = "unknown";
+
+    private String catalog = null;
+
+    /** ping SQL query */
+    private String pingQuery = null;
+
+    /** case-sensivity */
+    public enum CaseSensitivity { UNKNOWN, SENSITIVE, LOWERCASE, UPPERCASE }
+    private CaseSensitivity tablesCaseSensitivity = null;
+    private UnaryOperator<String> filterTableName = t -> t;
+
+    /** SQL query to set the current schema */
+    private String schemaQuery = null;
+
+    /** ID generation method */
+    public enum LastInsertIdPolicy { NONE, GENERATED_KEYS, RETURNING, QUERY, METHOD }
+    private LastInsertIdPolicy lastInsertIdPolicy = null;
+    private String lastInsertIdQuery = null;
+    private String lastInsertIdMethodName = null;
+
+    /** whether the JDBC driver is pedantic about column types */
+    private Boolean pedanticColumnTypes = null;
+
+    /** ignore tables matching this pattern */
+    private Pattern ignoreTablesPattern = null;
+
+    /** quoteIdentifier quote character */
+    private Character identifierQuoteChar = null;
+
+    /** whether driver supports ::varchar etc... */
+    private Boolean columnMarkers = null;
+
+    /**
+       * Get the last inserted id.
+       * @param statement source statement
+       * @param keyColumn key column name
+       * @return last inserted id (or -1)
+       * @throws SQLException
+       * /
+      public long getLastInsertId(Statement statement, String keyColumn) throws SQLException
+      {
+        long ret = -1;
+
+        if("mysql".equalsIgnoreCase(getTag()))
+        {    /* MySql * /
+          try
+          {
+            Method lastInsertId = statement.getClass().getMethod("getLastInsertID", new Class[0]);
+            ret = ((Long) lastInsertId.invoke(statement, new Object[0])).longValue();
+          }
+          catch (Throwable e)
+          {
+            logger.error("Could not find last insert id", e);
+          }
+        }
+        else if (getUsesGeneratedKeys())
+        {
+          int col = 1;
+          ResultSet rs = statement.getGeneratedKeys();
+          ResultSetMetaData rsmd = rs.getMetaData();
+          int numberOfColumns = rsmd.getColumnCount();
+          if (rs.next())
+          {
+            if (numberOfColumns > 1)
+            {
+              ret = rs.getLong(keyColumn);
+              if (rs.wasNull()) ret = -1;
+            }
+            else
+            {
+              ret = rs.getLong(1);
+              if (rs.wasNull()) ret = -1;
+            }
+          }
+        }
+        else
+        {
+          if (lastInsertIDQuery == null)
+          {
+            logger.error("getLastInsertID is not [yet] implemented for your dbms... Contribute!");
+          }
+          else
+          {
+            ResultSet rs = statement.getConnection().createStatement().executeQuery(lastInsertIDQuery);
+            rs.next();
+            ret = rs.getLong(1);
+            if (rs.wasNull()) ret = -1;
+          }
+        }
+        return ret;
+      }
+      */
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pool.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pool.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pool.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pool.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,32 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * This interface represents a generic pool of objects - no real need for now.
+ *
+ *  @author <a href='mailto:claude.brisson@gmail.com'>Claude Brisson</a>
+ *
+ */
+public interface Pool extends Serializable
+{
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pooled.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pooled.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pooled.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/Pooled.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,156 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * This abstract class represents a pooled object with a potential encapsulated resultset.
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public abstract class Pooled implements Serializable
+{
+    /**
+     * build a new pooled object.
+     */
+    public Pooled()
+    {
+        tagTime = System.currentTimeMillis();
+    }
+
+    /**
+     * get the time tag of this pooled object.
+     *
+     * @return the time tag
+     */
+    public long getTagTime()
+    {
+        return tagTime;
+    }
+
+    /**
+     * reset the time tag.
+     */
+    public void resetTagTime()
+    {
+        tagTime = System.currentTimeMillis();
+    }
+
+    /**
+     * notify this object that it is in use.
+     */
+    public void notifyInUse()
+    {
+        inUse = true;
+        resetTagTime();
+    }
+
+    /**
+     * notify this object that it is no more in use.
+     */
+    public void notifyOver()
+    {
+        try
+        {
+            if(resultSet != null && !resultSet.isClosed())
+            {
+                resultSet.close();
+            }
+        }
+        catch(SQLException sqle) {}    // ignore
+        resultSet = null;
+        inUse = false;
+    }
+
+    /**
+     * check whether this pooled object is in use.
+     *
+     * @return whether this object is in use
+     */
+    public boolean isInUse()
+    {
+        return inUse;
+    }
+
+    /**
+     * check whether this pooled object is marked as valid or invalid.
+     * (used in the recovery execute)
+     *
+     * @return whether this object is in use
+     */
+    public boolean isValid()
+    {
+        return valid;
+    }
+
+    /**
+     * definitely mark this statement as meant to be deleted.
+     */
+    public void setInvalid()
+    {
+        valid = false;
+    }
+
+    /**
+     * get the connection used by this statement.
+     *
+     * @return the connection used by this statement
+     */
+    public abstract ConnectionWrapper getConnection();
+
+    /**
+     * close this pooled object.
+     *
+     * @exception SQLException when thrown by the database engine
+     */
+    public abstract void close() throws SQLException;
+
+    /**
+     * time tag.
+     */
+    private long tagTime = 0;
+
+    // states (inUse - useOver) : (false-false) -> (true-false) -> (true-true) -> [delay] (false-false)
+
+    /**
+     * valid statement?
+     */
+    private boolean valid = true;
+
+    /**
+     * is this object in use?
+     */
+    private boolean inUse = false;
+
+    /**
+     * database connection.
+     */
+    protected ConnectionWrapper connection = null;
+
+    /**
+     * result set.
+     */
+    protected transient ResultSet resultSet = null;
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/PooledStatement.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/PooledStatement.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/PooledStatement.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/PooledStatement.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,231 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * this class encapsulates a jdbc PreparedStatement (and a potential ResultSet encapsulated by its base class).
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public class PooledStatement extends Pooled implements RowValues
+{
+    protected static Logger logger = LoggerFactory.getLogger(PooledStatement.class);
+
+    /** org.apache.velocity.tools.generic.ValueParser$ValueParserSub class, if found in the classpath. */
+    private static Class valueParserSubClass = null;
+
+    static
+    {
+        try
+        {
+            valueParserSubClass = Class.forName("org.apache.velocity.tools.generic.ValueParser$ValueParserSub");
+        }
+        catch(ClassNotFoundException cnfe) {}
+    }
+
+    /**
+     * build a new PooledStatement.
+     *
+     * @param connection database connection
+     * @param preparedStatement wrapped prepared statement
+     */
+    public PooledStatement(ConnectionWrapper connection, PreparedStatement preparedStatement)
+    {
+        this.connection = connection;
+        this.preparedStatement = preparedStatement;
+    }
+
+    /**
+     * check whether this pooled object is marked as valid or invalid.
+     * (used in the recovery execute)
+     *
+     * @return whether this object is in use
+     */
+    public boolean isValid()
+    {
+        return super.isValid() && preparedStatement != null;
+    }
+
+    public synchronized ResultSet executeQuery(Serializable... paramValues) throws SQLException
+    {
+        try
+        {
+            setParamValues(paramValues);
+            getConnection().enterBusyState();
+            return resultSet = preparedStatement.executeQuery();
+        }
+        finally
+        {
+            getConnection().leaveBusyState();
+        }
+    }
+
+    public synchronized int executeUpdate(Serializable... paramValues) throws SQLException
+    {
+        try
+        {
+            setParamValues(paramValues);
+            getConnection().enterBusyState();
+            return preparedStatement.executeUpdate();
+        }
+        finally
+        {
+            getConnection().leaveBusyState();
+        }
+    }
+
+
+    private void setParamValues(Serializable[] paramValues) throws SQLException
+    {
+        for (int i = 0; i < paramValues.length; ++i)
+        {
+            preparedStatement.setObject(i + 1, paramValues[i]);
+        }
+    }
+
+    /**
+     * issue the modification query of this prepared statement.
+     *
+     * @param params parameter values
+     * @exception SQLException thrown by the database engine
+     * @return the numer of affected rows
+     */
+    public synchronized int update(List params) throws SQLException
+    {
+        try
+        {
+            logger.trace("update-params={}", params);
+            setParams(params);
+            connection.enterBusyState();
+
+            int rows = preparedStatement.executeUpdate();
+
+            return rows;
+        }
+        finally
+        {
+            connection.leaveBusyState();
+            notifyOver();
+        }
+    }
+
+    /**
+     * get the object value of the specified resultset column.
+     *
+     * @param key the name of the resultset column
+     * @exception SQLException thrown by the database engine
+     * @return the object value returned by jdbc
+     */
+    public synchronized Serializable get(Object key) throws SQLException
+    {
+        if(!(key instanceof String) || resultSet == null)
+        {
+            return null;
+        }
+
+        Serializable ret = (Serializable)resultSet.getObject((String)key);
+
+        /*
+        if(entity != null && entity.isObfuscated((String)key))
+        {
+            ret = entity.obfuscate((ret));
+        }
+        */
+        return ret;
+    }
+
+    public Set<String> keySet() throws SQLException
+    {
+        if(resultSet == null) return new HashSet<String>();
+        return new HashSet<String>(SqlUtils.getColumnNames(resultSet));
+    }
+
+    /**
+     * get the last insert id.
+     *
+     * @exception SQLException thrown by the database engine
+     * @return the last insert id
+     */
+    public synchronized long getLastInsertID(String keyColumn) throws SQLException
+    {
+        return connection.getLastInsertId(preparedStatement, keyColumn);
+    }
+
+    /**
+     * close this statement.
+     *
+     * @exception SQLException thrown by the database engine
+     */
+    public synchronized void close() throws SQLException
+    {
+        if(preparedStatement != null)
+        {
+            preparedStatement.close();
+        }
+    }
+
+    /**
+     * get statement Connection.
+     *
+     *  @return the Connection object (usually a ConnectionWrapper object)
+     */
+    public ConnectionWrapper getConnection()
+    {
+        return connection;
+    }
+
+    /**
+     * set prepared parameter values.
+     *
+     * @param params parameter values
+     * @exception SQLException thrown by the database engine
+     */
+    private void setParams(List params) throws SQLException
+    {
+        for(int i = 0; i < params.size(); i++)
+        {
+            Object param = params.get(i);
+
+            if(valueParserSubClass != null && valueParserSubClass.isAssignableFrom(param.getClass()))
+            {
+                param = param.toString();
+            }
+            preparedStatement.setObject(i + 1, param);
+        }
+    }
+
+    /**
+     * wrapped prepared statement.
+     */
+    private transient PreparedStatement preparedStatement = null;
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/RowValues.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/RowValues.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/RowValues.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/RowValues.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,47 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.Set;
+
+/**
+ * This interface represents objects having read-only properties
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public interface RowValues
+{
+    /**
+     * get the property named key.
+     *
+     * @param key the name of the property to return
+     * @return the value of the property, or null if not found
+     */
+    Serializable get(Object key) throws SQLException;
+
+    /**
+     * Get keys set.
+     * @return keys set
+     */
+    Set<String> keySet() throws SQLException;
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/SqlUtils.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/SqlUtils.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/SqlUtils.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/SqlUtils.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,75 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * various SQL-related helpers.
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public class SqlUtils
+{
+    protected static Logger logger = LoggerFactory.getLogger(SqlUtils.class);
+
+    // that's crazy to have to code such a method...
+    // in Ruby for instance, it's :
+    // '*' * n
+    private static String stars(int length)
+    {
+        StringBuilder ret = new StringBuilder(length);
+        for(int i = 0; i < length; i++)
+        {
+            ret.append('*');
+        }
+        return ret.toString();
+    }
+
+    /**
+     * get the column nams of a result set
+     * @param resultSet result set
+     * @return list of columns
+     * @throws SQLException
+     */
+    public static List<String> getColumnNames(ResultSet resultSet) throws SQLException
+    {
+        List<String> columnNames = new ArrayList<String>();
+        ResultSetMetaData meta = resultSet.getMetaData();
+        int count = meta.getColumnCount();
+
+        for(int c = 1; c <= count; c++)
+        {
+            // see http://jira.springframework.org/browse/SPR-3541
+            // columnNames.add(meta.getColumnName(c));
+            columnNames.add(meta.getColumnLabel(c));
+        }
+        return columnNames;
+    }
+}

Added: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/StatementPool.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/StatementPool.java?rev=1857751&view=auto
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/StatementPool.java (added)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/sql/StatementPool.java Thu Apr 18 14:34:54 2019
@@ -0,0 +1,285 @@
+package org.apache.velocity.tools.model.sql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+
+import org.apache.velocity.tools.model.util.HashMultiMap;
+import org.apache.velocity.tools.model.util.MultiMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class is a pool of PooledPreparedStatements.
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ *
+ */
+public class StatementPool implements /* Runnable, */ Pool
+{
+    protected Logger logger = LoggerFactory.getLogger(StatementPool.class);
+
+    public StatementPool(ConnectionPool connectionPool)
+    {
+        this(connectionPool, false, 0);
+    }
+
+    /**
+     * build a new pool.
+     *
+     * @param connectionPool connection pool
+     */
+    public StatementPool(ConnectionPool connectionPool, boolean checkConnections, long checkInterval)
+    {
+        this.connectionPool = connectionPool;
+        this.checkConnections = checkConnections;
+        this.checkInterval = checkInterval;
+
+//      if(checkConnections) checkTimeoutThread = new Thread(this);
+//      checkTimeoutThread.start();
+    }
+
+    /**
+     * get a PooledStatement associated with this query.
+     *
+     * @param query an SQL query
+     * @exception SQLException thrown by the database engine
+     * @return a valid statement
+     */
+    protected synchronized PooledStatement prepareStatement(String query, boolean update) throws SQLException
+    {
+        logger.trace("prepare-" + query);
+
+        PooledStatement statement = null;
+        ConnectionWrapper connection = null;
+        List available = statementsMap.get(query);
+
+        for(Iterator it = available.iterator(); it.hasNext(); )
+        {
+            statement = (PooledStatement)it.next();
+            if(statement.isValid())
+            {
+                if(!statement.isInUse() &&!(connection = statement.getConnection()).isBusy())
+                {
+                    // check connection
+                  if(!connection.isClosed() && (!checkConnections || System.currentTimeMillis() - connection.getLastUse() < checkInterval || connection.check()))
+                    {
+                        statement.notifyInUse();
+                        return statement;
+                    }
+                    else
+                    {
+                        dropConnection(connection);
+                        it.remove();
+                    }
+                }
+            }
+            else
+            {
+                it.remove();
+            }
+        }
+        if(count == maxStatements)
+        {
+            throw new SQLException("Error: Too many opened prepared statements!");
+        }
+        connection = connectionPool.getConnection();
+        statement = new PooledStatement(connection,
+                update ?
+                    connection.prepareStatement(
+                            query, connection.getDriverInfos().getLastInsertIdPolicy() == DriverInfos.LastInsertIdPolicy.GENERATED_KEYS ?
+                                    Statement.RETURN_GENERATED_KEYS :
+                                    Statement.NO_GENERATED_KEYS) :
+                    connection.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY));
+        statementsMap.put(query, statement);
+        statement.notifyInUse();
+        return statement;
+    }
+
+    public synchronized PooledStatement prepareQuery(String query) throws SQLException
+    {
+        return prepareStatement(query, false);
+    }
+
+    public synchronized PooledStatement prepareUpdate(String query) throws SQLException
+    {
+        return prepareStatement(query, true);
+    }
+
+    /**
+     * cycle through statements to check and recycle them.
+     * 
+     * public void run() {
+     *   while (running) {
+     *       try {
+     *           Thread.sleep(checkDelay);
+     *       } catch (InterruptedException e) {}
+     *       long now = System.currentTimeMillis();
+     *       PooledStatement statement = null;
+     *       for (Iterator it=statementsMap.keySet().iterator();it.hasNext();)
+     *           for (Iterator jt=statementsMap.get(it.next()).iterator();jt.hasNext();) {
+     *               statement = (PooledStatement)jt.next();
+     *               if (statement.isInUse() && now-statement.getTagTime() > timeout)
+     *                   statement.notifyOver();
+     *           }
+     *   }
+     * }
+     */
+
+    /**
+     * close all statements.
+     */
+    public void clear()
+    {
+        // close all statements
+        for(Iterator it = statementsMap.keySet().iterator(); it.hasNext(); )
+        {
+            for(Iterator jt = statementsMap.get(it.next()).iterator(); jt.hasNext(); )
+            {
+                try
+                {
+                    ((PooledStatement)jt.next()).close();
+                }
+                catch(SQLException e)
+                {    // don't care now...
+                    logger.error("error while clearing pool", e);
+                }
+            }
+        }
+        statementsMap.clear();
+    }
+
+    /*
+     *  drop all statements relative to a specific connection
+     * @param connection the connection
+     */
+    private void dropConnection(ConnectionWrapper connection)
+    {
+        for(Iterator it = statementsMap.keySet().iterator(); it.hasNext(); )
+        {
+            for(Iterator jt = statementsMap.get(it.next()).iterator(); jt.hasNext(); )
+            {
+                PooledStatement statement = (PooledStatement)jt.next();
+
+                if(statement.getConnection() == connection)
+                {
+                    try
+                    {
+                        statement.close();
+                    }
+                    catch(SQLException sqle) {}
+                    statement.setInvalid();
+                }
+            }
+        }
+        try
+        {
+            connection.close();
+        }
+        catch(SQLException sqle) {}
+    }
+
+    /**
+     * clear statements on exit.
+     */
+    protected void finalize()
+    {
+        clear();
+    }
+
+    /**
+     * debug - get usage statistics.
+     *
+     * @return an int array : [nb of statements in use , total nb of statements]
+     */
+    public int[] getUsageStats()
+    {
+        int[] stats = new int[] { 0, 0 };
+
+        for(Iterator it = statementsMap.keySet().iterator(); it.hasNext(); )
+        {
+            for(Iterator jt = statementsMap.get(it.next()).iterator(); jt.hasNext(); )
+            {
+                if(!((PooledStatement)jt.next()).isInUse())
+                {
+                    stats[0]++;
+                }
+            }
+        }
+        stats[1] = statementsMap.size();
+        return stats;
+    }
+
+    /**
+     * connection pool.
+     */
+    private ConnectionPool connectionPool;
+
+    /**
+     * statements getCount.
+     */
+    private int count = 0;
+
+    /**
+     * map queries -> statements.
+     */
+    private MultiMap statementsMap = new HashMultiMap();    // query -> PooledStatement
+
+    /**
+     * running thread.
+     */
+    private Thread checkTimeoutThread = null;
+
+    /**
+     * true if running.
+     */
+    private boolean running = true;
+
+    /**
+     * do we need to check connections?
+     */
+    private boolean checkConnections = true;
+
+    /**
+     * minimal check interval
+     */
+    private long checkInterval;
+
+    /**
+     * check delay.
+     */
+
+//  private static final long checkDelay = 30*1000;
+
+    /**
+     * after this timeout, statements are recycled even if not closed.
+     */
+//  private static final long timeout = 60*60*1000;
+
+    /**
+     * max number of statements.
+     */
+    private static final int maxStatements = 50;
+}

Modified: velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/util/TypeUtils.java
URL: http://svn.apache.org/viewvc/velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/util/TypeUtils.java?rev=1857751&r1=1857750&r2=1857751&view=diff
==============================================================================
--- velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/util/TypeUtils.java (original)
+++ velocity/tools/branches/model/velocity-tools-model/src/main/java/org/apache/velocity/tools/model/util/TypeUtils.java Thu Apr 18 14:34:54 2019
@@ -21,8 +21,6 @@ package org.apache.velocity.tools.model.
 
 import org.apache.commons.codec.binary.Base64;
 
-import java.io.Serializable;
-import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.util.Calendar;
 import java.util.Date;