1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 package org.beiter.michael.db;
34
35 import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
36 import org.apache.commons.dbcp2.PoolableConnection;
37 import org.apache.commons.dbcp2.PoolableConnectionFactory;
38 import org.apache.commons.dbcp2.PoolingDataSource;
39 import org.apache.commons.lang3.Validate;
40 import org.apache.commons.pool2.impl.GenericObjectPool;
41 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import javax.naming.Context;
46 import javax.naming.InitialContext;
47 import javax.naming.NamingException;
48 import javax.sql.DataSource;
49 import java.util.Properties;
50 import java.util.concurrent.ConcurrentHashMap;
51 import java.util.concurrent.ConcurrentMap;
52
53
54
55
56
57
58
59
60 public final class DataSourceFactory {
61
62
63
64
65 private static final Logger LOG = LoggerFactory.getLogger(DataSourceFactory.class);
66
67
68
69
70
71 private static final ConcurrentHashMap<String, PoolingDataSource<PoolableConnection>> DS_POOLS =
72 new ConcurrentHashMap<>();
73
74
75
76
77 private DataSourceFactory() {
78 }
79
80
81
82
83
84
85
86
87
88
89 public static DataSource getDataSource(final String jndiName)
90 throws FactoryException {
91
92 Validate.notBlank(jndiName, "The validated character sequence 'jndiName' is null or empty");
93
94
95
96 try {
97
98 final Context context = new InitialContext();
99
100
101 final Object namedObject = context.lookup(jndiName);
102 if (DataSource.class.isInstance(namedObject)) {
103 final DataSource dataSource = (DataSource) context.lookup(jndiName);
104 context.close();
105
106 return dataSource;
107 } else {
108 final String error = "The JNDI name '" + jndiName + "' does not reference a SQL DataSource."
109 + " This is a configuration issue.";
110 LOG.warn(error);
111 throw new FactoryException(error);
112 }
113 } catch (NamingException e) {
114 final String error = "Error retrieving JDBC date source from JNDI: " + jndiName;
115 LOG.warn(error);
116 throw new FactoryException(error, e);
117 }
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 public static DataSource getDataSource(final ConnectionProperties poolSpec)
138 throws FactoryException {
139
140 Validate.notNull(poolSpec, "The validated object 'poolSpec' is null");
141 Validate.notBlank(poolSpec.getDriver(),
142 "The validated character sequence 'poolSpec.getDriver()' is null or empty");
143 Validate.notBlank(poolSpec.getUrl(), "The validated character sequence 'poolSpec.getUrl()' is null or empty");
144
145
146
147 final String driver = poolSpec.getDriver();
148 final String url = poolSpec.getUrl();
149
150
151 final String username = poolSpec.getUsername() == null ? "" : poolSpec.getUsername();
152 final String password = poolSpec.getPassword() == null ? "" : poolSpec.getPassword();
153
154
155
156 loadDriver(driver);
157
158
159 final ConcurrentMap<String, String> properties = new ConcurrentHashMap<>();
160 properties.put("user", username);
161 properties.put("password", password);
162
163
164
165 final String key = String.format("%s:%s", url, username);
166
167
168 if (!DS_POOLS.containsKey(key)) {
169 synchronized (DataSourceFactory.class) {
170 if (!DS_POOLS.containsKey(key)) {
171
172
173
174
175
176
177
178
179 DS_POOLS.putIfAbsent(key, getPoolingDataSource(url, properties, poolSpec));
180 }
181 }
182 }
183
184
185
186
187
188
189 return DS_POOLS.get(key);
190 }
191
192
193
194
195
196
197
198
199 public static void reset() {
200
201
202 DS_POOLS.clear();
203 }
204
205
206
207
208
209
210
211 private static void loadDriver(final String driver) throws FactoryException {
212
213
214 assert driver != null : "The driver cannot be null";
215
216 LOG.debug("Loading the database driver '" + driver + "'");
217
218
219 try {
220 Class.forName(driver);
221 } catch (ClassNotFoundException e) {
222 final String error = "Error loading JDBC driver class: " + driver;
223 LOG.warn(error, e);
224 throw new FactoryException(error, e);
225 }
226 }
227
228
229
230
231
232
233
234
235
236
237 private static PoolingDataSource<PoolableConnection> getPoolingDataSource(final String url,
238 final ConcurrentMap<String, String> properties,
239 final ConnectionProperties poolSpec) {
240
241
242 assert url != null : "The url cannot be null";
243 assert properties != null : "The properties cannot be null";
244 assert poolSpec != null : "The pol spec cannot be null";
245
246 LOG.debug("Creating new pooled data source for '" + url + "'");
247
248
249 final Properties props = new Properties();
250 props.putAll(properties);
251
252
253 final GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
254 poolConfig.setMaxTotal(poolSpec.getMaxTotal());
255 poolConfig.setMaxIdle(poolSpec.getMaxIdle());
256 poolConfig.setMinIdle(poolSpec.getMinIdle());
257 poolConfig.setMaxWaitMillis(poolSpec.getMaxWaitMillis());
258 poolConfig.setTestOnCreate(poolSpec.isTestOnCreate());
259 poolConfig.setTestOnBorrow(poolSpec.isTestOnBorrow());
260 poolConfig.setTestOnReturn(poolSpec.isTestOnReturn());
261 poolConfig.setTestWhileIdle(poolSpec.isTestWhileIdle());
262 poolConfig.setTimeBetweenEvictionRunsMillis(poolSpec.getTimeBetweenEvictionRunsMillis());
263 poolConfig.setNumTestsPerEvictionRun(poolSpec.getNumTestsPerEvictionRun());
264 poolConfig.setMinEvictableIdleTimeMillis(poolSpec.getMinEvictableIdleTimeMillis());
265 poolConfig.setSoftMinEvictableIdleTimeMillis(poolSpec.getSoftMinEvictableIdleTimeMillis());
266 poolConfig.setLifo(poolSpec.isLifo());
267
268
269
270 final org.apache.commons.dbcp2.ConnectionFactory connFactory = new DriverManagerConnectionFactory(url, props);
271 final PoolableConnectionFactory poolConnFactory = new PoolableConnectionFactory(connFactory, null);
272 poolConnFactory.setDefaultAutoCommit(poolSpec.isDefaultAutoCommit());
273 poolConnFactory.setDefaultReadOnly(poolSpec.isDefaultReadOnly());
274 poolConnFactory.setDefaultTransactionIsolation(poolSpec.getDefaultTransactionIsolation());
275 poolConnFactory.setCacheState(poolSpec.isCacheState());
276 poolConnFactory.setValidationQuery(poolSpec.getValidationQuery());
277 poolConnFactory.setMaxConnLifetimeMillis(poolSpec.getMaxConnLifetimeMillis());
278 final GenericObjectPool<PoolableConnection> connPool = new GenericObjectPool<>(poolConnFactory, poolConfig);
279 poolConnFactory.setPool(connPool);
280
281
282 return new PoolingDataSource<>(connPool);
283 }
284 }