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 }