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