1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.web.health;
17
18 import bitronix.tm.resource.jdbc.PoolingDataSource;
19 import com.codahale.metrics.Gauge;
20 import com.codahale.metrics.Metric;
21 import com.codahale.metrics.MetricSet;
22 import com.codahale.metrics.RatioGauge;
23 import org.apache.commons.dbcp.BasicDataSource;
24 import org.apache.log4j.Logger;
25 import org.enhydra.jdbc.pool.StandardXAPoolDataSource;
26
27 import javax.sql.DataSource;
28 import java.sql.SQLException;
29 import java.util.HashMap;
30 import java.util.Map;
31
32
33
34
35
36
37
38
39
40
41
42 public class DatabaseConnectionPoolMetricSet implements MetricSet {
43
44 private static final Logger LOG = Logger.getLogger(DatabaseConnectionPoolMetricSet.class);
45
46 public static final String ACTIVE = "pool.active";
47 public static final String MIN = "pool.min";
48 public static final String MAX = "pool.max";
49 public static final String USAGE = "pool.usage";
50
51 private final String namePrefix;
52 private final DataSource dataSource;
53
54
55
56
57
58
59 public DatabaseConnectionPoolMetricSet(String namePrefix, DataSource dataSource) {
60 this.namePrefix = namePrefix;
61 this.dataSource = dataSource;
62 }
63
64 @Override
65 public Map<String, Metric> getMetrics() {
66 Map<String, Metric> metrics = new HashMap<>();
67 boolean success = tryXAPool(metrics) || tryBitronix(metrics) || tryDBCP(metrics);
68 if (!success) {
69 LOG.warn("Failed to identify the type of connection pool with namePrefix: " + namePrefix + " and dataSource class: " + dataSource.getClass());
70 }
71 return metrics;
72 }
73
74 private boolean tryXAPool(Map<String, Metric> metrics) {
75 StandardXAPoolDataSource xaPoolDataSource = tryUnwrap(dataSource, StandardXAPoolDataSource.class);
76 if (xaPoolDataSource != null) {
77 installXAPoolMetrics(xaPoolDataSource, metrics);
78 return true;
79 }
80 return false;
81 }
82
83 private void installXAPoolMetrics(final StandardXAPoolDataSource dataSource, Map<String, Metric> metrics) {
84 metrics.put(namePrefix + ACTIVE, new Gauge<Integer>() {
85 @Override
86 public Integer getValue() {
87 return dataSource.getLockedObjectCount();
88 }
89 });
90 metrics.put(namePrefix + MIN, new Gauge<Integer>() {
91 @Override
92 public Integer getValue() {
93 return dataSource.getMinSize();
94 }
95 });
96 metrics.put(namePrefix + MAX, new Gauge<Integer>() {
97 @Override
98 public Integer getValue() {
99 return dataSource.getMaxSize();
100 }
101 });
102 metrics.put(namePrefix + USAGE, new RatioGauge() {
103 @Override
104 protected Ratio getRatio() {
105 return Ratio.of(dataSource.getLockedObjectCount(), dataSource.getMaxSize());
106 }
107 });
108 }
109
110 private boolean tryBitronix(Map<String, Metric> metrics) {
111 PoolingDataSource poolingDataSource = tryUnwrap(dataSource, PoolingDataSource.class);
112 if (poolingDataSource != null) {
113 installBitronixMetrics(poolingDataSource, metrics);
114 return true;
115 }
116 return false;
117 }
118
119 private void installBitronixMetrics(final PoolingDataSource dataSource, Map<String, Metric> metrics) {
120 metrics.put(namePrefix + ACTIVE, new Gauge<Integer>() {
121 @Override
122 public Integer getValue() {
123 return (int)dataSource.getTotalPoolSize() - (int)dataSource.getInPoolSize();
124 }
125 });
126 metrics.put(namePrefix + MIN, new Gauge<Integer>() {
127 @Override
128 public Integer getValue() {
129 return dataSource.getMinPoolSize();
130 }
131 });
132 metrics.put(namePrefix + MAX, new Gauge<Integer>() {
133 @Override
134 public Integer getValue() {
135 return dataSource.getMaxPoolSize();
136 }
137 });
138 metrics.put(namePrefix + USAGE, new RatioGauge() {
139 @Override
140 protected Ratio getRatio() {
141 return Ratio.of(dataSource.getTotalPoolSize() - dataSource.getInPoolSize(), dataSource.getMaxPoolSize());
142 }
143 });
144 }
145
146 private boolean tryDBCP(Map<String, Metric> metrics) {
147 BasicDataSource basicDataSource = tryUnwrap(dataSource, BasicDataSource.class);
148 if (basicDataSource != null) {
149 installDBCPMetrics(basicDataSource, metrics);
150 return true;
151 }
152 return false;
153 }
154
155 private void installDBCPMetrics(final BasicDataSource dataSource, Map<String, Metric> metrics) {
156 metrics.put(namePrefix + ACTIVE, new Gauge<Integer>() {
157 @Override
158 public Integer getValue() {
159 return dataSource.getNumActive();
160 }
161 });
162 metrics.put(namePrefix + MIN, new Gauge<Integer>() {
163 @Override
164 public Integer getValue() {
165 return dataSource.getMinIdle();
166 }
167 });
168 metrics.put(namePrefix + MAX, new Gauge<Integer>() {
169 @Override
170 public Integer getValue() {
171 return dataSource.getMaxActive();
172 }
173 });
174 metrics.put(namePrefix + USAGE, new RatioGauge() {
175 @Override
176 protected Ratio getRatio() {
177 return Ratio.of(dataSource.getNumActive(), dataSource.getMaxActive());
178 }
179 });
180 }
181
182 private <T> T tryUnwrap(DataSource dataSource, Class<T> targetType) {
183 if (targetType.isInstance(dataSource)) {
184 return targetType.cast(dataSource);
185 }
186 try {
187 if (dataSource.isWrapperFor(targetType)) {
188 return dataSource.unwrap(targetType);
189 }
190 } catch (SQLException e) {
191 LOG.warn("Exception when trying to unwrap datasource as " + targetType, e);
192 }
193 return null;
194 }
195
196 }