New test.
[mono.git] / mcs / class / IBM.Data.DB2 / IBM.Data.DB2 / DB2ConnectionPool.cs
1
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 // 
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 //
22 using System;\r
23 using System.Collections;\r
24 using System.Text;\r
25 using System.Threading;\r
26 \r
27 namespace IBM.Data.DB2\r
28 {\r
29         /// <summary>\r
30         /// \r
31         /// </summary>\r
32         /// <remarks>One connection pool per connectionstring</remarks>\r
33         internal sealed class DB2ConnectionPool\r
34         {\r
35                 private ArrayList       openFreeConnections; // list of pooled connections sorted by age. First connection is present at index 'connectionsUsableOffset'\r
36                 private Queue           openFreeMinimalAllocated;\r
37                 private int                     connectionsOpen;        // total number of connections open (in pool, an in use by application)\r
38                 private int                     connectionsInUse;       // total connection in use by application\r
39                 private int                     connectionsUsableOffset; // Offset to the first pooled connection in 'openFreeConnections'\r
40                 private Timer           timer;\r
41                 public string           databaseProductName;\r
42                 public string           databaseVersion;\r
43                 public int                      majorVersion;\r
44                 public int                      minorVersion;\r
45 \r
46                 private DB2ConnectionSettings connectionSettings;\r
47 \r
48                 public DB2ConnectionPool(DB2ConnectionSettings connectionSettings)\r
49                 {\r
50                         this.connectionSettings = connectionSettings;\r
51                         openFreeConnections = new ArrayList();\r
52                 }\r
53 \r
54                 public DB2ConnectionSettings ConnectionSettings\r
55                 {\r
56                         get { return connectionSettings; }\r
57                 }\r
58 \r
59                 public DB2OpenConnection GetOpenConnection(DB2Connection db2Conn)\r
60                 {\r
61                         DB2OpenConnection connection = null;\r
62                         lock(openFreeConnections.SyncRoot)\r
63                         {\r
64                                 if((connectionSettings.ConnectionPoolSizeMax > 0) &&\r
65                                         (connectionsOpen >= connectionSettings.ConnectionPoolSizeMax))\r
66                                 {\r
67                                         throw new ArgumentException("Maximum connections reached for connectionstring");\r
68                                 }\r
69 \r
70                                 while(connectionsOpen > connectionsInUse)\r
71                                 {\r
72                                         connection = (DB2OpenConnection)openFreeConnections[openFreeConnections.Count - 1];\r
73                                         openFreeConnections.RemoveAt(openFreeConnections.Count - 1);\r
74 \r
75                                         // check if connection is dead\r
76                                         int isDead;\r
77                                         short sqlRet = DB2CLIWrapper.SQLGetConnectAttr(connection.DBHandle, DB2Constants.SQL_ATTR_CONNECTION_DEAD, out isDead, 0, IntPtr.Zero);\r
78                                         if(((sqlRet == DB2Constants.SQL_SUCCESS_WITH_INFO) || (sqlRet == DB2Constants.SQL_SUCCESS)) &&\r
79                                                 (isDead == DB2Constants.SQL_CD_FALSE))\r
80                                         {\r
81                                                 connectionsInUse++;\r
82                                                 break;\r
83                                         }\r
84                                         else\r
85                                         {\r
86                                                 connectionsOpen--;\r
87                                                 connection.Dispose();\r
88                                                 connection = null;\r
89                                         }\r
90 \r
91                                 }\r
92                                 if(connectionsOpen == connectionsInUse)\r
93                                 {\r
94                                         if(timer != null)\r
95                                         {\r
96                                                 timer.Dispose();\r
97                                                 timer = null;\r
98                                         }\r
99                                 }\r
100                         }\r
101                         if(connection == null)\r
102                         {\r
103                                 openFreeConnections.Clear();\r
104                                 connectionsUsableOffset = 0;\r
105 \r
106                                 connection = new DB2OpenConnection(connectionSettings, db2Conn);\r
107                                 connectionsOpen++;\r
108                                 connectionsInUse++;\r
109                         }\r
110 \r
111                         return connection;\r
112                 }\r
113 \r
114                 private void DisposeTimedoutConnections(object state)\r
115                 {\r
116                         lock(openFreeConnections.SyncRoot)\r
117                         {\r
118                                 if(timer != null)\r
119                                 {\r
120                                         TimeSpan timeToDispose = TimeSpan.Zero;\r
121                                         DB2OpenConnection connection;\r
122                                         while(connectionsOpen > connectionsInUse)\r
123                                         {\r
124                                                 connection = (DB2OpenConnection)openFreeConnections[connectionsUsableOffset];\r
125                                                 timeToDispose = connection.poolDisposalTime.Subtract(DateTime.Now);\r
126                                                 if((timeToDispose.Ticks < 0) ||                                                          // time to die\r
127                                                         (timeToDispose > connectionSettings.ConnectionLifeTime)) // messing with system clock\r
128                                                 {\r
129                                                         connection.Dispose();\r
130                                                         openFreeConnections[connectionsUsableOffset] = null;\r
131                                                         connectionsOpen--;\r
132                                                         connectionsUsableOffset++;\r
133                                                 }\r
134                                                 else\r
135                                                 {\r
136                                                         break;\r
137                                                 }\r
138                                         }\r
139                                         if(connectionsOpen > connectionsInUse)\r
140                                         {\r
141                                                 connection = (DB2OpenConnection)openFreeConnections[connectionsUsableOffset];\r
142                                                 timer.Change(timeToDispose, new TimeSpan(-1));\r
143                                         }\r
144                                         else\r
145                                         {\r
146                                                 timer.Dispose();\r
147                                                 timer = null;\r
148                                         }\r
149                                 }\r
150                                 if((connectionsUsableOffset > (openFreeConnections.Capacity / 2)) &&\r
151                                         (connectionsOpen > connectionsInUse))\r
152                                 {\r
153                                         openFreeConnections.RemoveRange(0, connectionsUsableOffset); // cleanup once in a while\r
154                                         connectionsUsableOffset = 0;\r
155                                 }\r
156                         }\r
157                 }\r
158 \r
159                 public void AddToFreeConnections(DB2OpenConnection connection)\r
160                 {\r
161                         lock(openFreeConnections.SyncRoot)\r
162                         {\r
163                                 connection.poolDisposalTime = DateTime.Now.Add(connectionSettings.ConnectionLifeTime);\r
164                                 if(timer == null)\r
165                                 {\r
166                                         timer = new Timer(new TimerCallback(DisposeTimedoutConnections), null, \r
167                                                 connectionSettings.ConnectionLifeTime, new TimeSpan(-1));\r
168                                 }\r
169                                 connectionsInUse--;\r
170                                 openFreeConnections.Add(connection);\r
171                         }\r
172                 }\r
173 \r
174                 public void OpenConnectionFinalized()\r
175                 {\r
176                         lock(openFreeConnections.SyncRoot)\r
177                         {\r
178                                 connectionsOpen--;\r
179                                 connectionsInUse--;\r
180                         }\r
181                 }\r
182 \r
183                 /// <summary>\r
184                 /// Find a specific connection pool\r
185                 /// </summary>\r
186                 /// <param name="connectionString"></param>\r
187                 /// <returns></returns>\r
188                 static public DB2ConnectionPool FindConnectionPool(string connectionString)\r
189                 {\r
190                         return (DB2ConnectionPool)DB2Environment.Instance.connectionPools[connectionString];\r
191                 }\r
192 \r
193                 /// <summary>\r
194                 /// Get a connection pool. If it doesn't exist yet, create it\r
195                 /// </summary>\r
196                 /// <param name="connectionSettings"></param>\r
197                 /// <returns></returns>\r
198                 static public DB2ConnectionPool GetConnectionPool(DB2ConnectionSettings connectionSettings)\r
199                 {\r
200                         DB2Environment environment = DB2Environment.Instance;\r
201 \r
202                         lock(environment.connectionPools.SyncRoot)\r
203                         {\r
204                                 DB2ConnectionPool pool = (DB2ConnectionPool)environment.connectionPools[connectionSettings.ConnectionString];\r
205                                 if(pool == null)\r
206                                 {\r
207                                         pool = new DB2ConnectionPool(connectionSettings);\r
208                                         environment.connectionPools.Add(connectionSettings.ConnectionString, pool);\r
209                                 }\r
210                                 return pool;\r
211                         }\r
212                 }\r
213         }\r
214 }\r