1
18 package net.bull.javamelody;
19
20 import java.io.Serializable;
21 import java.lang.reflect.InvocationHandler;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Proxy;
25 import java.sql.Connection;
26 import java.sql.Driver;
27 import java.sql.SQLException;
28 import java.sql.Statement;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.concurrent.ConcurrentHashMap;
35 import java.util.concurrent.atomic.AtomicInteger;
36 import java.util.concurrent.atomic.AtomicLong;
37
38 import javax.naming.Context;
39 import javax.naming.NamingException;
40 import javax.servlet.ServletContext;
41 import javax.sql.DataSource;
42
43 import net.bull.javamelody.internal.common.LOG;
44 import net.bull.javamelody.internal.common.Parameters;
45 import net.bull.javamelody.internal.model.ConnectionInformations;
46 import net.bull.javamelody.internal.model.Counter;
47
48
54 public final class JdbcWrapper {
55
58 public static final JdbcWrapper SINGLETON = new JdbcWrapper(
59 new Counter(Counter.SQL_COUNTER_NAME, "db.png"));
60
61
62 static final AtomicInteger ACTIVE_CONNECTION_COUNT = new AtomicInteger();
63 static final AtomicInteger USED_CONNECTION_COUNT = new AtomicInteger();
64 static final AtomicLong TRANSACTION_COUNT = new AtomicLong();
65 static final AtomicInteger ACTIVE_THREAD_COUNT = new AtomicInteger();
66 static final AtomicInteger RUNNING_BUILD_COUNT = new AtomicInteger();
67 static final AtomicInteger BUILD_QUEUE_LENGTH = new AtomicInteger();
68 static final AtomicLong BUILD_QUEUE_WAITING_DURATIONS_SUM = new AtomicLong();
69 static final Map<Integer, ConnectionInformations> USED_CONNECTION_INFORMATIONS = new ConcurrentHashMap<>();
70
71 private static final int MAX_USED_CONNECTION_INFORMATIONS = 500;
72
73
74 private final Counter sqlCounter;
75 private ServletContext servletContext;
76 private boolean connectionInformationsEnabled;
77 private boolean jboss;
78 private boolean glassfish;
79 private boolean weblogic;
80
81 static final class ConnectionInformationsComparator
82 implements Comparator<ConnectionInformations>, Serializable {
83 private static final long serialVersionUID = 1L;
84
85
86 @Override
87 public int compare(ConnectionInformations connection1, ConnectionInformations connection2) {
88 return connection1.getOpeningDate().compareTo(connection2.getOpeningDate());
89 }
90 }
91
92
95 private class StatementInvocationHandler implements InvocationHandler {
96
97
98
99
100
101
102 private String requestName;
103 private final Statement statement;
104 private final Connection connection;
105
106 StatementInvocationHandler(String query, Statement statement, Connection connection) {
107 super();
108 assert statement != null;
109 assert connection != null;
110
111 this.requestName = query;
112 this.statement = statement;
113 this.connection = connection;
114 }
115
116
117 @Override
118 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
119
120 final String methodName = method.getName();
121 if (isEqualsMethod(methodName, args)) {
122 return statement.equals(args[0]);
123 } else if (isHashCodeMethod(methodName, args)) {
124 return statement.hashCode();
125 } else if (methodName.startsWith("execute")) {
126 if (isFirstArgAString(args)) {
127
128
129
130 requestName = (String) args[0];
131 } else if (("executeBatch".equals(methodName)
132 || "executeLargeBatch".equals(methodName)) && requestName != null
133 && !requestName.startsWith(" ")) {
134
135
136
137
138
139 requestName = " " + requestName;
140 }
141
142
143 requestName = String.valueOf(requestName);
144
145 return doExecute(requestName, statement, method, args);
146 } else if ("addBatch".equals(methodName) && isFirstArgAString(args)) {
147
148
149
150
151
152
153
154
155
156
157 requestName = (String) args[0];
158 } else if ("getConnection".equals(methodName) && (args == null || args.length == 0)) {
159 return connection;
160 }
161
162
163 return method.invoke(statement, args);
164 }
165
166 private boolean isFirstArgAString(Object[] args) {
167 return args != null && args.length > 0 && args[0] instanceof String;
168 }
169 }
170
171
174 private class ConnectionInvocationHandler implements InvocationHandler {
175 private final Connection connection;
176 private boolean alreadyClosed;
177
178 ConnectionInvocationHandler(Connection connection) {
179 super();
180 assert connection != null;
181 this.connection = connection;
182 }
183
184 void init() {
185
186 if (isConnectionInformationsEnabled()
187 && USED_CONNECTION_INFORMATIONS.size() < MAX_USED_CONNECTION_INFORMATIONS) {
188 USED_CONNECTION_INFORMATIONS.put(
189 ConnectionInformations.getUniqueIdOfConnection(connection),
190 new ConnectionInformations());
191 }
192 USED_CONNECTION_COUNT.incrementAndGet();
193 TRANSACTION_COUNT.incrementAndGet();
194 }
195
196
197 @Override
198 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
199
200 final String methodName = method.getName();
201 if (isEqualsMethod(methodName, args)) {
202 return areConnectionsEquals(args[0]);
203 } else if (isHashCodeMethod(methodName, args)) {
204 return connection.hashCode();
205 }
206 try {
207 Object result = method.invoke(connection, args);
208 if (result instanceof Statement) {
209 final String requestName;
210 if ("prepareStatement".equals(methodName) || "prepareCall".equals(methodName)) {
211
212
213 requestName = (String) args[0];
214 } else {
215 requestName = null;
216 }
217 result = createStatementProxy(requestName, (Statement) result,
218 (Connection) proxy);
219 }
220 return result;
221 } finally {
222 if ("close".equals(methodName) && !alreadyClosed) {
223 USED_CONNECTION_COUNT.decrementAndGet();
224 USED_CONNECTION_INFORMATIONS
225 .remove(ConnectionInformations.getUniqueIdOfConnection(connection));
226 alreadyClosed = true;
227 }
228 }
229 }
230
231 private boolean areConnectionsEquals(Object object) {
232
233
234 if (Proxy.isProxyClass(object.getClass())) {
235 final InvocationHandler invocationHandler = Proxy.getInvocationHandler(object);
236 if (invocationHandler instanceof DelegatingInvocationHandler) {
237 final DelegatingInvocationHandler d = (DelegatingInvocationHandler) invocationHandler;
238 if (d.getDelegate() instanceof ConnectionInvocationHandler) {
239 final ConnectionInvocationHandler c = (ConnectionInvocationHandler) d
240 .getDelegate();
241 return connection.equals(c.connection);
242 }
243 }
244 }
245 return connection.equals(object);
246 }
247 }
248
249 private static class ConnectionManagerInvocationHandler
250 extends AbstractInvocationHandler<Object> {
251
252 private static final long serialVersionUID = 1L;
253
254 ConnectionManagerInvocationHandler(Object javaxConnectionManager) {
255 super(javaxConnectionManager);
256 }
257
258 @Override
259 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
260 final Object result = method.invoke(getProxiedObject(), args);
261 if (result instanceof Connection) {
262 return SINGLETON
263 .createConnectionProxyOrRewrapIfJBossOrGlassfish((Connection) result);
264 }
265 return result;
266 }
267 }
268
269 private abstract static class AbstractInvocationHandler<T>
270 implements InvocationHandler, Serializable {
271 private static final long serialVersionUID = 1L;
272
273 @SuppressWarnings("all")
274 private final T proxiedObject;
275
276 AbstractInvocationHandler(T proxiedObject) {
277 super();
278 this.proxiedObject = proxiedObject;
279 }
280
281 T getProxiedObject() {
282 return proxiedObject;
283 }
284 }
285
286
287 private static class DelegatingInvocationHandler implements InvocationHandler, Serializable {
288
289 private static final long serialVersionUID = 7515240588169084785L;
290 @SuppressWarnings("all")
291 private final InvocationHandler delegate;
292
293 DelegatingInvocationHandler(InvocationHandler delegate) {
294 super();
295 this.delegate = delegate;
296 }
297
298 InvocationHandler getDelegate() {
299 return delegate;
300 }
301
302
303 @Override
304 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
305 try {
306 return delegate.invoke(proxy, method, args);
307 } catch (final InvocationTargetException e) {
308 if (e.getTargetException() != null) {
309 throw e.getTargetException();
310 }
311 throw e;
312 }
313 }
314 }
315
316 private JdbcWrapper(Counter sqlCounter) {
317 super();
318 assert sqlCounter != null;
319 this.sqlCounter = sqlCounter;
320
321 this.servletContext = null;
322 connectionInformationsEnabled = Parameters.isSystemActionsEnabled()
323 && !Parameters.isNoDatabase();
324 }
325
326 void initServletContext(ServletContext context) {
327 assert context != null;
328 this.servletContext = context;
329 final String serverInfo = servletContext.getServerInfo();
330 jboss = serverInfo.contains("JBoss") || serverInfo.contains("WildFly");
331 glassfish = serverInfo.contains("GlassFish")
332 || serverInfo.contains("Sun Java System Application Server")
333 || serverInfo.contains("Payara");
334 weblogic = serverInfo.contains("WebLogic");
335 connectionInformationsEnabled = Parameters.isSystemActionsEnabled()
336 && !Parameters.isNoDatabase();
337 }
338
339 public static int getUsedConnectionCount() {
340 return USED_CONNECTION_COUNT.get();
341 }
342
343 public static int getActiveConnectionCount() {
344 return ACTIVE_CONNECTION_COUNT.get();
345 }
346
347 public static long getTransactionCount() {
348 return TRANSACTION_COUNT.get();
349 }
350
351 public static int getActiveThreadCount() {
352 return ACTIVE_THREAD_COUNT.get();
353 }
354
355 public static int getRunningBuildCount() {
356 return RUNNING_BUILD_COUNT.get();
357 }
358
359 public static int getBuildQueueLength() {
360 return BUILD_QUEUE_LENGTH.get();
361 }
362
363 public static long getBuildQueueWaitingDurationsSum() {
364 return BUILD_QUEUE_WAITING_DURATIONS_SUM.get();
365 }
366
367 public static List<ConnectionInformations> getConnectionInformationsList() {
368 final List<ConnectionInformations> result = new ArrayList<>(
369 USED_CONNECTION_INFORMATIONS.values());
370 Collections.sort(result, new ConnectionInformationsComparator());
371 return Collections.unmodifiableList(result);
372 }
373
374 public Counter getSqlCounter() {
375 return sqlCounter;
376 }
377
378 boolean isConnectionInformationsEnabled() {
379 return connectionInformationsEnabled;
380 }
381
382 public static int getMaxConnectionCount() {
383 return JdbcWrapperHelper.getMaxConnectionCount();
384 }
385
386 public static Map<String, Map<String, Object>> getBasicDataSourceProperties() {
387 return JdbcWrapperHelper.getBasicDataSourceProperties();
388 }
389
390 public static Map<String, DataSource> getJndiAndSpringDataSources() throws NamingException {
391 return JdbcWrapperHelper.getJndiAndSpringDataSources();
392 }
393
394
399 public static void registerSpringDataSource(String name, DataSource dataSource) {
400 JdbcWrapperHelper.registerSpringDataSource(name, dataSource);
401 }
402
403 Object doExecute(String requestName, Statement statement, Method method, Object[] args)
404 throws IllegalAccessException, InvocationTargetException {
405 assert requestName != null;
406 assert statement != null;
407 assert method != null;
408
409
410 if (!sqlCounter.isDisplayed() || requestName.startsWith("explain ")) {
411 ACTIVE_CONNECTION_COUNT.incrementAndGet();
412 try {
413 return method.invoke(statement, args);
414 } finally {
415 ACTIVE_CONNECTION_COUNT.decrementAndGet();
416 }
417 }
418
419 final long start = System.currentTimeMillis();
420 boolean systemError = true;
421 try {
422 ACTIVE_CONNECTION_COUNT.incrementAndGet();
423
424
425
426 sqlCounter.bindContext(requestName, requestName, null, -1, -1);
427
428 final Object result = method.invoke(statement, args);
429 systemError = false;
430 return result;
431 } catch (final InvocationTargetException e) {
432 if (e.getCause() instanceof SQLException) {
433 final int errorCode = ((SQLException) e.getCause()).getErrorCode();
434 if (errorCode >= 20000 && errorCode < 30000) {
435
436
437
438
439 systemError = false;
440 }
441 }
442 throw e;
443 } finally {
444
445
446
447
448
449 ACTIVE_CONNECTION_COUNT.decrementAndGet();
450 final long duration = Math.max(System.currentTimeMillis() - start, 0);
451 sqlCounter.addRequest(requestName, duration, -1, -1, systemError, -1);
452 }
453 }
454
455 boolean rebindDataSources() {
456 boolean ok;
457
458
459 try {
460 final boolean rewrapDataSources = Parameter.REWRAP_DATASOURCES.getValueAsBoolean();
461 if (rewrapDataSources || Parameter.DATASOURCES.getValue() != null) {
462
463
464 stop();
465 }
466 final Map<String, DataSource> jndiDataSources = JdbcWrapperHelper.getJndiDataSources();
467 LOG.debug("datasources found in JNDI: " + jndiDataSources.keySet());
468 for (final Map.Entry<String, DataSource> entry : jndiDataSources.entrySet()) {
469 final String jndiName = entry.getKey();
470 final DataSource dataSource = entry.getValue();
471 try {
472 if (rewrapDataSources || isServerNeedsRewrap(jndiName)) {
473 rewrapDataSource(jndiName, dataSource);
474 JdbcWrapperHelper.registerRewrappedDataSource(jndiName, dataSource);
475 } else if (!isProxyAlready(dataSource)) {
476
477 final DataSource dataSourceProxy = createDataSourceProxy(jndiName,
478 dataSource);
479 JdbcWrapperHelper.rebindDataSource(servletContext, jndiName, dataSource,
480 dataSourceProxy);
481 LOG.debug("datasource rebinded: " + jndiName + " from class "
482 + dataSource.getClass().getName() + " to class "
483 + dataSourceProxy.getClass().getName());
484 }
485 } catch (final Throwable t) {
486
487 LOG.debug("rebinding datasource " + jndiName + " failed, skipping it", t);
488 }
489 }
490 ok = true;
491 } catch (final Throwable t) {
492
493 LOG.debug("rebinding datasources failed, skipping", t);
494 ok = false;
495 }
496 return ok;
497 }
498
499 private void rewrapDataSource(String jndiName, DataSource dataSource)
500 throws IllegalAccessException {
501 final String dataSourceClassName = dataSource.getClass().getName();
502 LOG.debug("Datasource needs rewrap: " + jndiName + " of class " + dataSourceClassName);
503 final String dataSourceRewrappedMessage = "Datasource rewrapped: " + jndiName;
504 if (isJBossOrGlassfishDataSource(dataSourceClassName)) {
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 Object javaxConnectionManager = JdbcWrapperHelper.getFieldValue(dataSource, "cm");
520 javaxConnectionManager = createJavaxConnectionManagerProxy(javaxConnectionManager);
521 JdbcWrapperHelper.setFieldValue(dataSource, "cm", javaxConnectionManager);
522 LOG.debug(dataSourceRewrappedMessage);
523 } else if (isWildfly9DataSource(dataSourceClassName)) {
524 Object delegateDataSource = JdbcWrapperHelper.getFieldValue(dataSource, "delegate");
525 delegateDataSource = createDataSourceProxy((DataSource) delegateDataSource);
526 JdbcWrapperHelper.setFieldValue(dataSource, "delegate", delegateDataSource);
527 LOG.debug(dataSourceRewrappedMessage);
528 } else if (weblogic
529 && "weblogic.jdbc.common.internal.RmiDataSource".equals(dataSourceClassName)) {
530
531
532 rewrapWebLogicDataSource(dataSource);
533 LOG.debug(dataSourceRewrappedMessage);
534 } else if (isDbcpDataSource(dataSourceClassName)) {
535
536
537
538
539
540
541
542 rewrapBasicDataSource(dataSource);
543 LOG.debug(dataSourceRewrappedMessage);
544 } else if ("org.apache.openejb.resource.jdbc.managed.local.ManagedDataSource"
545 .equals(dataSourceClassName)) {
546
547 rewrapTomEEDataSource(dataSource);
548 LOG.debug(dataSourceRewrappedMessage);
549 } else {
550 LOG.info("Datasource can't be rewrapped: " + jndiName + " of class "
551 + dataSourceClassName);
552 }
553 }
554
555 private boolean isServerNeedsRewrap(String jndiName) {
556 return glassfish || jboss || weblogic || jndiName.contains("openejb");
557 }
558
559 private boolean isDbcpDataSource(String dataSourceClassName) {
560 return "org.apache.tomcat.dbcp.dbcp.BasicDataSource".equals(dataSourceClassName)
561 || "org.apache.tomcat.dbcp.dbcp2.BasicDataSource".equals(dataSourceClassName)
562 || "org.apache.commons.dbcp.BasicDataSource".equals(dataSourceClassName)
563 || "org.apache.commons.dbcp2.BasicDataSource".equals(dataSourceClassName)
564 || "org.apache.openejb.resource.jdbc.BasicManagedDataSource"
565 .equals(dataSourceClassName)
566 || "org.apache.openejb.resource.jdbc.BasicDataSource".equals(dataSourceClassName);
567 }
568
569 private boolean isJBossOrGlassfishDataSource(String dataSourceClassName) {
570 return jboss
571 && "org.jboss.resource.adapter.jdbc.WrapperDataSource".equals(dataSourceClassName)
572 || jboss && "org.jboss.jca.adapters.jdbc.WrapperDataSource"
573 .equals(dataSourceClassName)
574 || glassfish && "com.sun.gjc.spi.jdbc40.DataSource40".equals(dataSourceClassName);
575 }
576
577 private boolean isWildfly9DataSource(String dataSourceClassName) {
578 return jboss && "org.jboss.as.connector.subsystems.datasources.WildFlyDataSource"
579 .equals(dataSourceClassName);
580 }
581
582 private void rewrapWebLogicDataSource(DataSource dataSource) throws IllegalAccessException {
583 if (JdbcWrapperHelper.hasField(dataSource, "delegate")) {
584
585 final Object delegate = JdbcWrapperHelper.getFieldValue(dataSource, "delegate");
586 rewrapWebLogicDataSource((DataSource) delegate);
587 } else {
588 Object jdbcCtx = JdbcWrapperHelper.getFieldValue(dataSource, "jdbcCtx");
589 if (jdbcCtx != null) {
590 jdbcCtx = createContextProxy((Context) jdbcCtx);
591 JdbcWrapperHelper.setFieldValue(dataSource, "jdbcCtx", jdbcCtx);
592 }
593 Object driverInstance = JdbcWrapperHelper.getFieldValue(dataSource, "driverInstance");
594 if (driverInstance != null) {
595 driverInstance = createDriverProxy((Driver) driverInstance);
596 JdbcWrapperHelper.setFieldValue(dataSource, "driverInstance", driverInstance);
597 }
598 }
599 }
600
601 private void rewrapBasicDataSource(DataSource dataSource) throws IllegalAccessException {
602
603
604
605 try {
606 dataSource.getConnection().close();
607 } catch (final Exception e) {
608 LOG.debug(e.toString());
609
610
611 }
612 Object innerDataSource = JdbcWrapperHelper.getFieldValue(dataSource, "dataSource");
613 if (innerDataSource != null) {
614 innerDataSource = createDataSourceProxy((DataSource) innerDataSource);
615 JdbcWrapperHelper.setFieldValue(dataSource, "dataSource", innerDataSource);
616 }
617 }
618
619 private void rewrapTomEEDataSource(DataSource dataSource) throws IllegalAccessException {
620
621
622
623 try {
624 dataSource.getConnection().close();
625 } catch (final Exception e) {
626 LOG.debug(e.toString());
627
628
629 }
630 Object innerDataSource = JdbcWrapperHelper.getFieldValue(dataSource, "delegate");
631 if (innerDataSource != null) {
632 innerDataSource = createDataSourceProxy((DataSource) innerDataSource);
633 JdbcWrapperHelper.setFieldValue(dataSource, "delegate", innerDataSource);
634 }
635 }
636
637 boolean stop() {
638 boolean ok;
639 try {
640 JdbcWrapperHelper.rebindInitialDataSources(servletContext);
641
642
643 final Map<String, DataSource> rewrappedDataSources = JdbcWrapperHelper
644 .getRewrappedDataSources();
645 for (final Map.Entry<String, DataSource> entry : rewrappedDataSources.entrySet()) {
646 final String jndiName = entry.getKey();
647 final DataSource dataSource = entry.getValue();
648 unwrapDataSource(jndiName, dataSource);
649 }
650 rewrappedDataSources.clear();
651
652 JdbcWrapperHelper.clearProxyCache();
653
654 ok = true;
655 } catch (final Throwable t) {
656
657 LOG.debug("rebinding initial datasources failed, skipping", t);
658 ok = false;
659 }
660 return ok;
661 }
662
663 private void unwrapDataSource(String jndiName, DataSource dataSource)
664 throws IllegalAccessException {
665 final String dataSourceClassName = dataSource.getClass().getName();
666 LOG.debug("Datasource needs unwrap: " + jndiName + " of class " + dataSourceClassName);
667 final String dataSourceUnwrappedMessage = "Datasource unwrapped: " + jndiName;
668 if (isJBossOrGlassfishDataSource(dataSourceClassName)) {
669 unwrap(dataSource, "cm", dataSourceUnwrappedMessage);
670 } else if (isWildfly9DataSource(dataSourceClassName)) {
671 unwrap(dataSource, "delegate", dataSourceUnwrappedMessage);
672 } else if (weblogic
673 && "weblogic.jdbc.common.internal.RmiDataSource".equals(dataSourceClassName)) {
674 if (JdbcWrapperHelper.hasField(dataSource, "delegate")) {
675
676 final Object delegate = JdbcWrapperHelper.getFieldValue(dataSource, "delegate");
677 unwrap(delegate, "jdbcCtx", dataSourceUnwrappedMessage);
678 unwrap(delegate, "driverInstance", dataSourceUnwrappedMessage);
679 } else {
680 unwrap(dataSource, "jdbcCtx", dataSourceUnwrappedMessage);
681 unwrap(dataSource, "driverInstance", dataSourceUnwrappedMessage);
682 }
683 } else if (isDbcpDataSource(dataSourceClassName)) {
684 unwrap(dataSource, "dataSource", dataSourceUnwrappedMessage);
685 }
686 }
687
688 private void unwrap(Object parentObject, String fieldName, String unwrappedMessage)
689 throws IllegalAccessException {
690 final Object proxy = JdbcWrapperHelper.getFieldValue(parentObject, fieldName);
691 if (proxy != null && Proxy.isProxyClass(proxy.getClass())) {
692 InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
693 if (invocationHandler instanceof DelegatingInvocationHandler) {
694 invocationHandler = ((DelegatingInvocationHandler) invocationHandler).getDelegate();
695 if (invocationHandler instanceof AbstractInvocationHandler) {
696 final Object proxiedObject = ((AbstractInvocationHandler<?>) invocationHandler)
697 .getProxiedObject();
698 JdbcWrapperHelper.setFieldValue(parentObject, fieldName, proxiedObject);
699 LOG.debug(unwrappedMessage);
700 }
701 }
702 }
703 }
704
705 Context createContextProxy(final Context context) {
706 assert context != null;
707 final InvocationHandler invocationHandler = new AbstractInvocationHandler<Context>(
708 context) {
709 private static final long serialVersionUID = 1L;
710
711
712 @Override
713 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
714 Object result = method.invoke(context, args);
715 if (result instanceof DataSource) {
716 result = createDataSourceProxy((DataSource) result);
717 }
718 return result;
719 }
720 };
721 return createProxy(context, invocationHandler);
722 }
723
724
725 private Driver createDriverProxy(final Driver driver) {
726 assert driver != null;
727 final InvocationHandler invocationHandler = new AbstractInvocationHandler<Driver>(driver) {
728 private static final long serialVersionUID = 1L;
729
730
731 @Override
732 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
733 Object result = method.invoke(driver, args);
734 if (result instanceof Connection) {
735 result = createConnectionProxy((Connection) result);
736 }
737 return result;
738 }
739 };
740 return createProxy(driver, invocationHandler);
741 }
742
743
744 private Object createJavaxConnectionManagerProxy(Object javaxConnectionManager) {
745 assert javaxConnectionManager != null;
746 final InvocationHandler invocationHandler = new ConnectionManagerInvocationHandler(
747 javaxConnectionManager);
748 return createProxy(javaxConnectionManager, invocationHandler);
749 }
750
751 void rewrapConnection(Connection connection) throws IllegalAccessException {
752 assert connection != null;
753 if (jboss && connection.getClass().getSimpleName().startsWith("WrappedConnection")) {
754
755
756
757 final Object baseWrapperManagedConnection = JdbcWrapperHelper.getFieldValue(connection,
758 "mc");
759 final String conFieldName = "con";
760 Connection con = (Connection) JdbcWrapperHelper
761 .getFieldValue(baseWrapperManagedConnection, conFieldName);
762
763 if (!isProxyAlready(con)) {
764 con = createConnectionProxy(con);
765 JdbcWrapperHelper.setFieldValue(baseWrapperManagedConnection, conFieldName, con);
766 }
767 } else if (glassfish && ("com.sun.gjc.spi.jdbc40.ConnectionHolder40"
768 .equals(connection.getClass().getName())
769 || "com.sun.gjc.spi.jdbc40.ConnectionWrapper40"
770 .equals(connection.getClass().getName())
771 || "com.sun.gjc.spi.jdbc40.ProfiledConnectionWrapper40"
772 .equals(connection.getClass().getName()))) {
773
774
775
776
777 final String conFieldName = "con";
778 Connection con = (Connection) JdbcWrapperHelper.getFieldValue(connection, conFieldName);
779
780 if (!isProxyAlready(con)) {
781 con = createConnectionProxy(con);
782 JdbcWrapperHelper.setFieldValue(connection, conFieldName, con);
783 }
784 }
785 }
786
787
792 public DataSource createDataSourceProxy(DataSource dataSource) {
793 return createDataSourceProxy(null, dataSource);
794 }
795
796
802 public DataSource createDataSourceProxy(String name, final DataSource dataSource) {
803 assert dataSource != null;
804 JdbcWrapperHelper.pullDataSourceProperties(name, dataSource);
805 final InvocationHandler invocationHandler = new AbstractInvocationHandler<DataSource>(
806 dataSource) {
807 private static final long serialVersionUID = 1L;
808
809
810 @Override
811 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
812 Object result = method.invoke(dataSource, args);
813 if (result instanceof Connection) {
814 result = createConnectionProxy((Connection) result);
815 }
816 return result;
817 }
818 };
819 return createProxy(dataSource, invocationHandler);
820 }
821
822 Connection createConnectionProxyOrRewrapIfJBossOrGlassfish(Connection connection)
823 throws IllegalAccessException {
824 if (jboss || glassfish) {
825 rewrapConnection(connection);
826 return connection;
827 }
828 return createConnectionProxy(connection);
829 }
830
831
836 public Connection createConnectionProxy(Connection connection) {
837 assert connection != null;
838
839
840 if (isMonitoringDisabled()) {
841 return connection;
842 }
843 final ConnectionInvocationHandler invocationHandler = new ConnectionInvocationHandler(
844 connection);
845 final Connection result = createProxy(connection, invocationHandler);
846 if (result != connection) {
847 invocationHandler.init();
848 }
849 return result;
850 }
851
852 boolean isSqlMonitoringDisabled() {
853 return isMonitoringDisabled() || !sqlCounter.isDisplayed();
854 }
855
856 private static boolean isMonitoringDisabled() {
857
858
859 return Parameter.DISABLED.getValueAsBoolean();
860 }
861
862 Statement createStatementProxy(String query, Statement statement, Connection connection) {
863 assert statement != null;
864
865
866
867
868
869
870
871
872 final InvocationHandler invocationHandler = new StatementInvocationHandler(query, statement,
873 connection);
874 return createProxy(statement, invocationHandler);
875 }
876
877 static boolean isEqualsMethod(Object methodName, Object[] args) {
878
879 return "equals" == methodName && args != null && args.length == 1;
880 }
881
882 static boolean isHashCodeMethod(Object methodName, Object[] args) {
883
884 return "hashCode" == methodName && (args == null || args.length == 0);
885 }
886
887 static <T> T createProxy(T object, InvocationHandler invocationHandler) {
888 return createProxy(object, invocationHandler, null);
889 }
890
891 static <T> T createProxy(T object, InvocationHandler invocationHandler,
892 List<Class<?>> interfaces) {
893 if (isProxyAlready(object)) {
894
895
896
897 return object;
898 }
899 final InvocationHandler ih = new DelegatingInvocationHandler(invocationHandler);
900 return JdbcWrapperHelper.createProxy(object, ih, interfaces);
901 }
902
903 static boolean isProxyAlready(Object object) {
904 return Proxy.isProxyClass(object.getClass()) && Proxy.getInvocationHandler(object)
905 .getClass().getName().equals(DelegatingInvocationHandler.class.getName());
906
907
908
909 }
910
911 }
912