68
|
1 /*
|
|
2 Copyright (c) 2008 Franklin Schmidt <fschmidt@gmail.com>
|
|
3
|
|
4 Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5 of this software and associated documentation files (the "Software"), to deal
|
|
6 in the Software without restriction, including without limitation the rights
|
|
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8 copies of the Software, and to permit persons to whom the Software is
|
|
9 furnished to do so, subject to the following conditions:
|
|
10
|
|
11 The above copyright notice and this permission notice shall be included in
|
|
12 all copies or substantial portions of the Software.
|
|
13
|
|
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
20 THE SOFTWARE.
|
|
21 */
|
|
22
|
|
23 package fschmidt.db.pool;
|
|
24
|
|
25 import java.sql.Connection;
|
|
26 import java.sql.SQLException;
|
|
27 import java.sql.Statement;
|
|
28 import java.util.List;
|
|
29 import java.util.ArrayList;
|
|
30 import java.util.Set;
|
|
31 import java.util.HashSet;
|
|
32 import org.slf4j.Logger;
|
|
33 import org.slf4j.LoggerFactory;
|
|
34 import fschmidt.db.DbDatabase;
|
|
35 import fschmidt.db.DbObject;
|
|
36 import fschmidt.db.DbKey;
|
|
37 import fschmidt.db.DbObjectFactory;
|
|
38 import fschmidt.db.SQLRuntimeException;
|
|
39 import fschmidt.db.extend.DbDatabaseExt;
|
|
40 import fschmidt.db.extend.DbTableExt;
|
|
41 import fschmidt.db.extend.DbTransaction;
|
|
42 import fschmidt.db.extend.DbRecordExt;
|
|
43 import fschmidt.db.extend.FilterDatabase;
|
|
44 import fschmidt.util.java.Stack;
|
|
45 import fschmidt.util.java.ArrayStack;
|
|
46
|
|
47
|
|
48 public final class Pool {
|
|
49 private static final Logger logger = LoggerFactory.getLogger(Pool.class);
|
|
50
|
|
51 final Stack<PooledConnection> stack = new ArrayStack<PooledConnection>();
|
|
52 private long idleTimeout = 0L;
|
|
53 private long idleExpires;
|
|
54 final ThreadLocal<PooledConnection> localCon = new ThreadLocal<PooledConnection>();
|
|
55
|
|
56 public long getIdleTimeout() {
|
|
57 return idleTimeout;
|
|
58 }
|
|
59
|
|
60 public void setIdleTimeout(long idleTimeout) {
|
|
61 this.idleTimeout = idleTimeout;
|
|
62 }
|
|
63
|
|
64 boolean isInTransaction() {
|
|
65 PooledConnection con = localCon.get();
|
|
66 return con!=null && con.isInTransaction();
|
|
67 }
|
|
68
|
|
69 void commitTransaction() {
|
|
70 localCon.get().commitTransaction();
|
|
71 }
|
|
72
|
|
73 void endTransaction() {
|
|
74 PooledConnection con = localCon.get();
|
|
75 if( con==null )
|
|
76 throw new IllegalStateException("endTransaction called without beginTransaction");
|
|
77 con.endTransaction();
|
|
78 }
|
|
79
|
|
80 void runBeforeCommit(Runnable r) {
|
|
81 PooledConnection con = localCon.get();
|
|
82 if( !con.ignoreRunnablesInThisTransaction )
|
|
83 con.beforeCommitList.add(r);
|
|
84 }
|
|
85
|
|
86 void runJustAfterCommit(Runnable r) {
|
|
87 PooledConnection con = localCon.get();
|
|
88 if( !con.ignoreRunnablesInThisTransaction )
|
|
89 con.afterCommitList.add(0,r);
|
|
90 }
|
|
91
|
|
92 void runAfterCommit(Runnable r) {
|
|
93 PooledConnection con = localCon.get();
|
|
94 if( !con.ignoreRunnablesInThisTransaction )
|
|
95 con.afterCommitList.add(r);
|
|
96 }
|
|
97
|
|
98 void ignoreRunnablesInThisTransaction() {
|
|
99 if( !isInTransaction() )
|
|
100 throw new IllegalStateException("not in transaction");
|
|
101 localCon.get().ignoreRunnablesInThisTransaction = true;
|
|
102 }
|
|
103
|
|
104 DbTransaction getTransaction() {
|
|
105 if( isInTransaction() ) {
|
|
106 return localCon.get().dbTrans;
|
|
107 } else {
|
|
108 return null;
|
|
109 }
|
|
110 }
|
|
111
|
|
112 Connection getConnection(DbDatabaseImpl db)
|
|
113 throws SQLException
|
|
114 {
|
|
115 PooledConnection con = localCon.get();
|
|
116 if( con==null || con.con().isClosed() ) {
|
|
117 con = getConnection2(db);
|
|
118 localCon.set(con);
|
|
119 pools.get().add(this);
|
|
120 }
|
|
121 return con.nest(db.user);
|
|
122 }
|
|
123
|
|
124 private static final long timeout = 1000L*60*60; // 1 hour
|
|
125
|
|
126 private synchronized PooledConnection getConnection2(DbDatabaseImpl db)
|
|
127 throws SQLException
|
|
128 {
|
|
129 long now = System.currentTimeMillis();
|
|
130 while( !stack.isEmpty() ) {
|
|
131 PooledConnection con = stack.pop();
|
|
132 Connection realCon = con.con();
|
|
133 if( realCon.isClosed() )
|
|
134 continue;
|
|
135 try {
|
|
136 if( now - con.lastUsed > timeout ) {
|
|
137 Statement stmt = realCon.createStatement();
|
|
138 stmt.executeQuery("select 1");
|
|
139 stmt.close();
|
|
140 }
|
|
141 } catch(SQLException e) {
|
|
142 logger.info("corrupt connection dropped from pool");
|
|
143 continue;
|
|
144 }
|
|
145 return con;
|
|
146 }
|
|
147 idleExpires = now + idleTimeout;
|
|
148 return new PooledConnection(db);
|
|
149 }
|
|
150
|
|
151 boolean isExpired(long lastUsed) {
|
|
152 return idleTimeout > 0 && lastUsed > idleExpires;
|
|
153 }
|
|
154
|
|
155 private static ThreadLocal<Set<Pool>> pools = new ThreadLocal<Set<Pool>>() {
|
|
156 protected Set<Pool> initialValue() {
|
|
157 return new HashSet<Pool>();
|
|
158 }
|
|
159 };
|
|
160
|
|
161 public static void threadReset() {
|
|
162 Set<Pool> set = pools.get();
|
|
163 for( Pool pool : set ) {
|
|
164 PooledConnection con = pool.localCon.get();
|
|
165 if( con != null ) {
|
|
166 con.forceClose();
|
|
167 }
|
|
168 }
|
|
169 set.clear();
|
|
170 }
|
|
171
|
|
172 public Connection getNativeConnection() {
|
|
173 return localCon.get().con();
|
|
174 }
|
|
175
|
|
176 }
|