View Javadoc

1   /*
2    ~ Copyright 2006-2007 Nicolas De Loof.
3    ~
4    ~ Licensed under the Apache License, Version 2.0 (the "License");
5    ~ you may not use this file except in compliance with the License.
6    ~ You may obtain a copy of the License at
7    ~
8    ~      http://www.apache.org/licenses/LICENSE-2.0
9    ~
10   ~ Unless required by applicable law or agreed to in writing, software
11   ~ distributed under the License is distributed on an "AS IS" BASIS,
12   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   ~ See the License for the specific language governing permissions and
14   ~ limitations under the License.
15   */
16  package org.jmonit.support.jdbc;
17  
18  import java.sql.SQLException;
19  import java.util.StringTokenizer;
20  
21  import org.jmonit.Monitor;
22  import org.jmonit.Monitoring;
23  
24  /**
25   * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
26   */
27  public class JdbcMonitor
28  {
29      /**
30       * The root jdbc monitor
31       */
32      private Monitor monitor;
33  
34      /**
35       * Constructor
36       * 
37       * @param monitor The monitor used as root for JDBC monitoring
38       */
39      public JdbcMonitor( Monitor monitor )
40      {
41          super();
42          this.monitor = monitor.tag( "jdbc" );
43      }
44  
45      public Monitor getConnectionMonitor()
46      {
47          return this.monitor;
48      }
49  
50      /**
51       * @param sql SQL request
52       * @return a monitor for this statement
53       */
54      public Monitor getStatementMonitor( String sql )
55      {
56          return getSubMonitor( generalizeSql( sql ), "statement" );
57      }
58  
59      /**
60       * @param sql SQL request
61       * @return a monitor for this preapredStatement
62       */
63      public Monitor getPreparedStatementMonitor( String sql )
64      {
65          return getSubMonitor( sql, "preparedStatement" );
66      }
67  
68      /**
69       * @param sql SQL request
70       * @return a monitor for this callableStatement
71       */
72      public Monitor getCallableStatementMonitor( String sql )
73      {
74          return getSubMonitor( sql, "callableStatement" );
75      }
76  
77      public void monitorSQLException( SQLException sqle )
78      {
79          Monitoring.add( monitor.getName() + ".SQLException" + sqle.getErrorCode(), 1 );
80      }
81  
82      private Monitor getSubMonitor( String sql, String type )
83      {
84          StringBuffer name = new StringBuffer( monitor.getName() );
85          name.append( "." ).append( type ).append( "~" ).append( sql );
86  
87          return Monitoring.getMonitor( name.toString() ).tag( "jdbc" ).tag( type );
88      }
89  
90      /**
91       * Convert a SQL query to a "generic" query by removing any hard coded
92       * value. This makes the SQL look like a PreparedStatement.
93       * 
94       * @param sql SQL query
95       * @return generalized SQL query
96       */
97      protected String generalizeSql( String sql )
98      {
99          StringBuffer stb = new StringBuffer();
100         StringTokenizer tokenizer = new StringTokenizer( sql, " \t\r\n,)=", true );
101         boolean ws = false;
102         while ( tokenizer.hasMoreTokens() )
103         {
104             String token = tokenizer.nextToken();
105             if ( token.length() == 1 && Character.isWhitespace( token.charAt( 0 ) ) )
106             {
107                 if ( !ws )
108                 {
109                     stb.append( " " );
110                 }
111                 ws = true;
112                 continue;
113             }
114             ws = false;
115             char first = token.charAt( 0 );
116             char last = token.charAt( token.length() - 1 );
117             if ( first == last && ( first == '\'' || first == '"' ) )
118             {
119                 // Quoted value
120                 token = "?";
121             }
122             else
123             {
124                 boolean numeric = true;
125                 char[] chars = token.toCharArray();
126                 boolean dot = false;
127                 for ( int i = 0; i < chars.length; i++ )
128                 {
129                     if ( !Character.isDigit( chars[i] ) && !dot && chars[i] != '.' )
130                     {
131                         numeric = false;
132                         break;
133                     }
134                     if ( chars[i] == '.' )
135                     {
136                         dot = true;
137                     }
138                 }
139                 if ( numeric )
140                 {
141                     // Hard coded Numeric
142                     token = "?";
143                 }
144             }
145             stb.append( token );
146         }
147         return stb.toString().toLowerCase();
148     }
149 }