1//
2// SessionPool.cpp
3//
4// Library: SQL
5// Package: SessionPooling
6// Module: SessionPool
7//
8// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/SQL/SessionPool.h"
16#include "Poco/SQL/SessionFactory.h"
17#include "Poco/SQL/SQLException.h"
18#include <algorithm>
19
20
21namespace Poco {
22namespace SQL {
23
24
25SessionPool::SessionPool(const std::string& connector, const std::string& connectionString, int minSessions, int maxSessions, int idleTime):
26 _connector(connector),
27 _connectionString(connectionString),
28 _minSessions(minSessions),
29 _maxSessions(maxSessions),
30 _idleTime(idleTime),
31 _nSessions(0),
32 _janitorTimer(1000*idleTime, 1000*idleTime/4),
33 _shutdown(false)
34{
35 Poco::TimerCallback<SessionPool> callback(*this, &SessionPool::onJanitorTimer);
36 _janitorTimer.start(callback);
37}
38
39
40SessionPool::~SessionPool()
41{
42 try
43 {
44 shutdown();
45 }
46 catch (...)
47 {
48 poco_unexpected();
49 }
50}
51
52
53Session SessionPool::get(const std::string& rName, bool value)
54{
55 Session s = get();
56 _addFeatureMap.insert(AddFeatureMap::value_type(s.impl(),
57 std::make_pair(rName, s.getFeature(rName))));
58 s.setFeature(rName, value);
59
60 return s;
61}
62
63
64Session SessionPool::get()
65{
66 if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
67
68 purgeDeadSessions();
69 Poco::Mutex::ScopedLock lock(_mutex);
70 if (_idleSessions.empty())
71 {
72 if (_nSessions < _maxSessions)
73 {
74 Session newSession(SessionFactory::instance().create(_connector, _connectionString));
75 applySettings(newSession.impl());
76 customizeSession(newSession);
77
78 PooledSessionHolderPtr pHolder(new PooledSessionHolder(*this, newSession.impl()));
79 _idleSessions[pHolder.get()] = pHolder;
80 ++_nSessions;
81 }
82 else throw SessionPoolExhaustedException(_connector);
83 }
84
85 PooledSessionHolderPtr pHolder(_idleSessions.begin()->second);
86 PooledSessionImplPtr pPSI(new PooledSessionImpl(pHolder));
87
88 _activeSessions[pHolder.get()] = pHolder;
89 _idleSessions.erase(pHolder.get());
90 return Session(pPSI);
91}
92
93
94void SessionPool::purgeDeadSessions()
95{
96 if (_shutdown) return;
97
98 Poco::Mutex::ScopedLock lock(_mutex);
99 SessionList::iterator it = _idleSessions.begin();
100 for (; it != _idleSessions.end(); )
101 {
102 PooledSessionHolderPtr pHolder = it->second;
103 if (!pHolder->session()->isConnected())
104 {
105 it = _idleSessions.erase(it);
106 --_nSessions;
107 }
108 else ++it;
109 }
110}
111
112
113int SessionPool::capacity() const
114{
115 return _maxSessions;
116}
117
118
119int SessionPool::used() const
120{
121 Poco::Mutex::ScopedLock lock(_mutex);
122 return (int) _activeSessions.size();
123}
124
125
126int SessionPool::idle() const
127{
128 Poco::Mutex::ScopedLock lock(_mutex);
129 return (int) _idleSessions.size();
130}
131
132
133int SessionPool::dead()
134{
135 int count = 0;
136
137 Poco::Mutex::ScopedLock lock(_mutex);
138 SessionList::iterator it = _activeSessions.begin();
139 SessionList::iterator itEnd = _activeSessions.end();
140 for (; it != itEnd; ++it)
141 {
142 PooledSessionHolderPtr pHolder = it->second;
143 if (!pHolder->session()->isConnected())
144 ++count;
145 }
146
147 return count;
148}
149
150
151int SessionPool::allocated() const
152{
153 Poco::Mutex::ScopedLock lock(_mutex);
154 return _nSessions;
155}
156
157
158int SessionPool::available() const
159{
160 if (_shutdown) return 0;
161 return _maxSessions - used();
162}
163
164
165void SessionPool::setFeature(const std::string& rName, bool state)
166{
167 if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
168
169 Poco::Mutex::ScopedLock lock(_mutex);
170 if (_nSessions > 0)
171 throw InvalidAccessException("Features can not be set after the first session was created.");
172
173 _featureMap.insert(FeatureMap::value_type(rName, state));
174}
175
176
177bool SessionPool::getFeature(const std::string& rName)
178{
179 if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
180
181 FeatureMap::const_iterator it = _featureMap.find(rName);
182 if (_featureMap.end() == it)
183 throw NotFoundException("Feature not found:" + rName);
184
185 return it->second;
186}
187
188
189void SessionPool::setProperty(const std::string& rName, const Poco::Any& value)
190{
191 if (_shutdown) throw InvalidAccessException("Session pool has been shut down.");
192
193 Poco::Mutex::ScopedLock lock(_mutex);
194 if (_nSessions > 0)
195 throw InvalidAccessException("Properties can not be set after first session was created.");
196
197 _propertyMap.insert(PropertyMap::value_type(rName, value));
198}
199
200
201Poco::Any SessionPool::getProperty(const std::string& rName)
202{
203 PropertyMap::const_iterator it = _propertyMap.find(rName);
204 if (_propertyMap.end() == it)
205 throw NotFoundException("Property not found:" + rName);
206
207 return it->second;
208}
209
210
211void SessionPool::applySettings(SessionImpl::Ptr pImpl)
212{
213 FeatureMap::iterator fmIt = _featureMap.begin();
214 FeatureMap::iterator fmEnd = _featureMap.end();
215 for (; fmIt != fmEnd; ++fmIt) pImpl->setFeature(fmIt->first, fmIt->second);
216
217 PropertyMap::iterator pmIt = _propertyMap.begin();
218 PropertyMap::iterator pmEnd = _propertyMap.end();
219 for (; pmIt != pmEnd; ++pmIt) pImpl->setProperty(pmIt->first, pmIt->second);
220}
221
222
223void SessionPool::customizeSession(Session&)
224{
225}
226
227
228void SessionPool::putBack(PooledSessionHolderPtr pHolder)
229{
230 if (_shutdown) return;
231
232 Poco::Mutex::ScopedLock lock(_mutex);
233
234 PooledSessionHolder* psh = pHolder.get();
235 SessionList::iterator it = _activeSessions.find(psh);
236 if (it != _activeSessions.end())
237 {
238 if (pHolder->session()->isConnected())
239 {
240 pHolder->session()->reset();
241
242 // reverse settings applied at acquisition time, if any
243 AddPropertyMap::iterator pIt = _addPropertyMap.find(pHolder->session());
244 if (pIt != _addPropertyMap.end())
245 pHolder->session()->setProperty(pIt->second.first, pIt->second.second);
246
247 AddFeatureMap::iterator fIt = _addFeatureMap.find(pHolder->session());
248 if (fIt != _addFeatureMap.end())
249 pHolder->session()->setFeature(fIt->second.first, fIt->second.second);
250
251 // re-apply the default pool settings
252 applySettings(pHolder->session());
253
254 pHolder->access();
255 _idleSessions[pHolder.get()] = pHolder;
256 }
257 else --_nSessions;
258
259 _activeSessions.erase(it);
260 }
261 else
262 {
263 it = _idleSessions.find(psh);
264 if (it != _idleSessions.end()) return;
265 poco_bugcheck_msg("Unknown session passed to SessionPool::putBack()");
266 }
267}
268
269
270void SessionPool::onJanitorTimer(Poco::Timer&)
271{
272 if (_shutdown) return;
273
274 Poco::Mutex::ScopedLock lock(_mutex);
275
276 SessionList::iterator it = _idleSessions.begin();
277 while (_nSessions > _minSessions && it != _idleSessions.end())
278 {
279 PooledSessionHolderPtr pHolder = it->second;
280 if (pHolder->idle() > _idleTime || !pHolder->session()->isConnected())
281 {
282 try { pHolder->session()->close(); }
283 catch (...) { }
284 it = _idleSessions.erase(it);
285 --_nSessions;
286 }
287 else ++it;
288 }
289}
290
291
292void SessionPool::shutdown()
293{
294 if (_shutdown) return;
295 Poco::Mutex::ScopedLock lock(_mutex);
296 _shutdown = true;
297 _janitorTimer.stop();
298 closeAll(_idleSessions);
299 closeAll(_activeSessions);
300}
301
302
303void SessionPool::closeAll(SessionList& sessionList)
304{
305 SessionList::iterator it = sessionList.begin();
306 for (; it != sessionList.end();)
307 {
308 PooledSessionHolderPtr pHolder = it->second;
309 try { pHolder->session()->close(); }
310 catch (...) { }
311 it = sessionList.erase(it);
312 if (_nSessions > 0) --_nSessions;
313 }
314}
315
316
317} } // namespace Poco::SQL
318