001    /*
002    @license.text@
003    */
004    
005    package biz.hammurapi.sql;
006    
007    import java.io.PrintWriter;
008    import java.sql.CallableStatement;
009    import java.sql.Connection;
010    import java.sql.DatabaseMetaData;
011    import java.sql.PreparedStatement;
012    import java.sql.SQLException;
013    import java.sql.SQLWarning;
014    import java.sql.Savepoint;
015    import java.sql.Statement;
016    import java.util.ArrayList;
017    import java.util.Collection;
018    import java.util.HashMap;
019    import java.util.Map;
020    
021    import javax.sql.DataSource;
022    
023    /**
024     * Wraps another datasource for re-using already allocated thread connections.
025     * Maintains one connection per thread/user name/pwd. Connection is allocated on first 
026     * getConnection() call. Every next call increments use counter. 
027     * Connection closes when it is not used (counter==0).
028     *  
029     * @author Pavel Vlasov
030     */
031    public class ConnectionPerThreadDataSourceFilter implements DataSource {
032            
033            // Key for connection without user name and password.
034            private Object key = new Object();
035            
036            private ThreadLocal connectionTL=new ThreadLocal() {
037                    protected Object initialValue() {
038                            return new HashMap();
039                    }
040            };
041            
042            private DataSource master;
043            
044            /**
045             * Constructor
046             * @param master Master data source
047             * @param initConnectionTransaction optional transaction to initialize connection
048             * e.g. set database schema.
049             */
050            public ConnectionPerThreadDataSourceFilter(DataSource master) {
051                    this.master = master;
052            }
053                                    
054            public int getLoginTimeout() throws SQLException {
055                    return master.getLoginTimeout();
056            }
057            
058            public void setLoginTimeout(int seconds) throws SQLException {
059                    master.setLoginTimeout(seconds);        
060            }
061            
062            public PrintWriter getLogWriter() throws SQLException {
063                    return master.getLogWriter();
064            }
065            
066            public void setLogWriter(PrintWriter out) throws SQLException {
067                    master.setLogWriter(out);
068            }
069            
070            public Connection getConnection() throws SQLException {
071                    Map threadMap = (Map) connectionTL.get();
072                    Connection ret = (Connection) threadMap.get(key);
073                    if (ret==null) {
074                            ret = new ConnectionWrapper(master.getConnection(), key, threadMap);
075                    }
076                    return ret;
077            }
078            
079            public Connection getConnection(String user, final String password) throws SQLException {
080                    Collection key = new ArrayList();
081                    key.add(user);
082                    key.add(password);
083                    Map threadMap = (Map) connectionTL.get();
084                    Connection ret = (Connection) threadMap.get(key);
085                    if (ret==null) {
086                            ret = new ConnectionWrapper(master.getConnection(), key, threadMap);
087                    }
088                    return ret;
089            }
090            
091            private class ConnectionWrapper implements Connection {
092                    Connection master;
093                    
094                    private ConnectionWrapper(Connection master, Object key, Map threadMap) {
095                            super();
096                            this.master = master;
097                            this.key = key;
098                            this.threadMap = threadMap;
099                    }
100                    
101                    public void clearWarnings() throws SQLException {
102                            master.clearWarnings();
103                    }
104                    public void close() throws SQLException {
105                            counter--;
106                            if (counter==0) {
107                                    master.close();
108                                    master = null;
109                                    threadMap.remove(key);
110                            }
111                    }
112                    public void commit() throws SQLException {
113                            master.commit();
114                    }
115                    public Statement createStatement() throws SQLException {
116                            return master.createStatement();
117                    }
118                    public Statement createStatement(int resultSetType,
119                                    int resultSetConcurrency, int resultSetHoldability)
120                                    throws SQLException {
121                            return master.createStatement(resultSetType, resultSetConcurrency,
122                                            resultSetHoldability);
123                    }
124                    public Statement createStatement(int resultSetType,
125                                    int resultSetConcurrency) throws SQLException {
126                            return master.createStatement(resultSetType, resultSetConcurrency);
127                    }
128                    public boolean getAutoCommit() throws SQLException {
129                            return master.getAutoCommit();
130                    }
131                    public String getCatalog() throws SQLException {
132                            return master.getCatalog();
133                    }
134                    public int getHoldability() throws SQLException {
135                            return master.getHoldability();
136                    }
137                    public DatabaseMetaData getMetaData() throws SQLException {
138                            return master.getMetaData();
139                    }
140                    public int getTransactionIsolation() throws SQLException {
141                            return master.getTransactionIsolation();
142                    }
143                    public Map getTypeMap() throws SQLException {
144                            return master.getTypeMap();
145                    }
146                    public SQLWarning getWarnings() throws SQLException {
147                            return master.getWarnings();
148                    }
149                    public boolean isClosed() throws SQLException {
150                            return counter==0 || (master!=null && master.isClosed());
151                    }
152                    public boolean isReadOnly() throws SQLException {
153                            return master.isReadOnly();
154                    }
155                    public String nativeSQL(String sql) throws SQLException {
156                            return master.nativeSQL(sql);
157                    }
158                    public CallableStatement prepareCall(String sql, int resultSetType,
159                                    int resultSetConcurrency, int resultSetHoldability)
160                                    throws SQLException {
161                            return master.prepareCall(sql, resultSetType, resultSetConcurrency,
162                                            resultSetHoldability);
163                    }
164                    public CallableStatement prepareCall(String sql, int resultSetType,
165                                    int resultSetConcurrency) throws SQLException {
166                            return master.prepareCall(sql, resultSetType, resultSetConcurrency);
167                    }
168                    public CallableStatement prepareCall(String sql) throws SQLException {
169                            return master.prepareCall(sql);
170                    }
171                    public PreparedStatement prepareStatement(String sql,
172                                    int resultSetType, int resultSetConcurrency,
173                                    int resultSetHoldability) throws SQLException {
174                            return master.prepareStatement(sql, resultSetType,
175                                            resultSetConcurrency, resultSetHoldability);
176                    }
177                    public PreparedStatement prepareStatement(String sql,
178                                    int resultSetType, int resultSetConcurrency)
179                                    throws SQLException {
180                            return master.prepareStatement(sql, resultSetType,
181                                            resultSetConcurrency);
182                    }
183                    public PreparedStatement prepareStatement(String sql,
184                                    int autoGeneratedKeys) throws SQLException {
185                            return master.prepareStatement(sql, autoGeneratedKeys);
186                    }
187                    public PreparedStatement prepareStatement(String sql,
188                                    int[] columnIndexes) throws SQLException {
189                            return master.prepareStatement(sql, columnIndexes);
190                    }
191                    public PreparedStatement prepareStatement(String sql,
192                                    String[] columnNames) throws SQLException {
193                            return master.prepareStatement(sql, columnNames);
194                    }
195                    public PreparedStatement prepareStatement(String sql)
196                                    throws SQLException {
197                            return master.prepareStatement(sql);
198                    }
199                    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
200                            master.releaseSavepoint(savepoint);
201                    }
202                    public void rollback() throws SQLException {
203                            master.rollback();
204                    }
205                    public void rollback(Savepoint savepoint) throws SQLException {
206                            master.rollback(savepoint);
207                    }
208                    public void setAutoCommit(boolean autoCommit) throws SQLException {
209                            master.setAutoCommit(autoCommit);
210                    }
211                    public void setCatalog(String catalog) throws SQLException {
212                            master.setCatalog(catalog);
213                    }
214                    public void setHoldability(int holdability) throws SQLException {
215                            master.setHoldability(holdability);
216                    }
217                    public void setReadOnly(boolean readOnly) throws SQLException {
218                            master.setReadOnly(readOnly);
219                    }
220                    public Savepoint setSavepoint() throws SQLException {
221                            return master.setSavepoint();
222                    }
223                    public Savepoint setSavepoint(String name) throws SQLException {
224                            return master.setSavepoint(name);
225                    }
226                    public void setTransactionIsolation(int level) throws SQLException {
227                            master.setTransactionIsolation(level);
228                    }
229                    public void setTypeMap(Map arg0) throws SQLException {
230                            master.setTypeMap(arg0);
231                    }
232                    
233                    int counter=1;
234                    Object key;
235                    Map threadMap;
236            }
237            
238    }
239