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