com.jamonapi.proxy.MonProxyFactoryImp Maven / Gradle / Ivy
package com.jamonapi.proxy;
import com.jamonapi.JAMonBufferListener;
import com.jamonapi.JAMonListener;
import com.jamonapi.Monitor;
import com.jamonapi.MonitorFactory;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.*;
/**
* MonProxyFactory allows developers to monitor ANY interface by simply passing the Object implementing
* the interface to the monitor method. (note the object passed MUST implement an interface or it will
* be a runtime error). A great use of this is to monitor jdbc interfaces and to aid in this there are
* overloaded methods that take Connections, Statements, PreparedStatements, CallableStatements, and
* ResultSets. These overloaded methods take advantage of knowledege of SQL and track additional statistics.
* The following capabilities can be acquired by using MonProxyFactory. All can individually be enabled/disabled.
*
* This is a nonstatic version and is the primary implementation. The static version MonProxyFactory simply calls this version
*
* - Overall monitoring can be enabled/disabled by calling MonProxyFactory.enable(...). This will enable/disable monitors.
* You can start out by enabling all monitors and disabling the ones you wish or vice versa.
*
*
- All methods of a given interface will have a jamon rows. Any jamon row will have the label (in this
* case the method signature), hits, avg/min/max execution time, active/avg active/max active, last value and
* more. By default interface monitoring is on. It can be enabled/disabled by calling MonProxyFactory.enableInterfaceM(...).
* JDBC classes such as Connections, Statements, PreparedStatemetns, CallableStatements, and ResultsSets
* will also automatically be monitored if the class returning them was monitored. If you don't wish this
* to occur then you can use the monitor method that takes an Object.
*
*
- ResultSet interfaces can be monitored however methods like getObject and next are called so much and
* typically don't cause performance problems, so there is a seperate enable/disable capability for them. Note for
* ResultSet monitoring to occur interface monitoring must also be enabled.
* However it can be enabled if interface monitoring is enabled and MonProxyFactory.enabledResultSets(true) is called.
* ResultSet's are by default not monitored.
*
*
- SQLSummary will add a jamon row for all sql text issued against a PreparedStatement, CallableStatement,
* or Statement. Argument values are replaced with ? for Statements to ensure the logical queries are matched.
* PreparedStatements need not be changed to do this, but Statements may look a little different
* For example: select * from table where name='jeff beck' would become select * from table where name=?
* This is a powerful monitor as it allows you to see hits, avg/min/max query times for all queries in your
* application. This is enabled/disabled by calling MonProxyFactory.enableSQLSummary(...).
*
*
- SQLDetail puts the last N (configurable) queries that have been run into a rolling buffer. The SQL buffer
* will have the actual query in the case of a Statement and the version with ? for PreparedStatements. In addition
* other stats will be in this buffer such as how long the query took to execute, how many times has the PreparedStatement
* been reused, the jdbc method that executed the sql, and the exception stack trace if it occured. This can be enabled/disabled
* by calling MonProxyFactory.enableSQLDetail(...)
*
*
- Exception Summary will add several jamon rows when an exception occurs. Note the Exception buffer is used for any kind of Exception
* including SQLExceptions. The exceptions added are 1. One containing the method that through the exception as well as the exception.
* 2. One indicating how many exceptions total have been thrown through proxies, 3) One containing the exception type that was thrown.
* This can be enabled/disabled by calling MonProxyFactory.enableExceptionSummary(...)
*
*
- ExceptionDetail puts the last N (configurable) exceptions that have occured to any interface that is being monitored into a buffer.
* The stack trace is in the row as well as when it was thrown and what method threw it. This can be enabled/disabled by calling
* MonProxyFactory.enableExceptionDetail(...).
*
*
*
* Sample code:
* {@code
* ResultSet rs= MonProxyFactory.monitor(resultSet);
* Connection conn=MonProxyFactory.monitor(connection);
* MyInterface my=(MyInterface) MonProxyFactory.monitor(myObject);//myObject implements MyInterface
* YourInterface your=(YourInterface) MonProxyFactory.monitor(yourObject);//myObject implements MyInterface
* }
*
*
*/
public class MonProxyFactoryImp {
private static final String EXCEPTION = "Exception";
private final Class[] CLASS_ARRAY=new Class[0];
private Params params=new Params();
private MonProxyLabelerInt labelFactory=new MonProxyLabeler();
/** This class will be called when creating the jamon labels for both standard summary as well as
* exceptions.
*
* @param factory
*/
public void setLabelFactory(MonProxyLabelerInt factory) {
labelFactory=factory;
}
public MonProxyLabelerInt getLabelFactory() {
return labelFactory;
}
/** Returns the MonProxy invocation handler should you need access to its methods like setLabeler(...) etc.
* Note if the handler is not a MonProxy handler than a class cast exception will be thrown. This is possible as
* the method takes a generic Proxy as an argument. being as the monitor methods return an Object, ResultSet etc. these
* must be cast to a Proxy before calling this method. For example.
*
* {@code
* ResultSet rsc=MonProxyFactory.monitor(resultSet);
* MonProxy monProxy=MonProxyFactory.getMonProxy((Proxy)rsc);
* }
*
* @param proxy
* @return MonProxy
*/
public MonProxy getMonProxy(Proxy proxy) {
return (MonProxy) Proxy.getInvocationHandler(proxy);
}
/** By passing any interface to the monitor method, all public method calls and exceptions
* will be monitored. It will be a runtime error if the Object passed does not implement an interface. Note that
* only interfaces implemented directly by the passed in Object are monitored. Should you want to cast to an interface
* implemented by a base class to the passed in Object you should call one of the more explicit monitor(..) methods
*
* Sample call:
* MyInterface myProxyObject=(MyInterface) MonProxyFactory.monitor(myObject);
*/
public Object monitor(Object object) {
if (!isEnabled() || object==null) // if not enabled return the original object unchanged, not the proxy
return object;
else
return monitorNoCheck (object, getInterfaces(object.getClass()));// proxy will implement ALL interfaces of this class
}
/** By passing any interface to the monitor method, and an array of interfaces to implement then all public method calls and exceptions
* will be monitored. It will be a runtime error if the Object passed does not implement an interface.
*
* Sample call:
* MyInterface myProxyObject=(MyInterface) MonProxyFactory.monitor(myObject, ineterfaces);
*/
public Object monitor(Object object, Class[] interfaces) {
if (!isEnabled() || object==null) // if not enabled return the original object unchanged, not the proxy
return object;
else
return monitorNoCheck(object, interfaces);
}
// no check means it creates monitored object without seeing if object is null or service is enabled. This is a private method so that
// is ok
private Object monitorNoCheck(Object object, Class[] interfaces) {
MonProxy monProxy=new MonProxy(object, params, (MonProxyLabelerInt) labelFactory.clone());
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
interfaces,// proxy will implement ALL interfaces in array
monProxy
);
}
/** By passing any interface to the monitor method, and an interface to implement then all public method calls and exceptions
* will be monitored. It will be a runtime error if the Object passed does not implement an interface.
*
* Sample call:
* MyInterface myProxyObject=(MyInterface) MonProxyFactory.monitor(myObject, com.mypackage.MyInterface.class);
*/
public Object monitor(Object object, Class iface) {
return monitor (object, new Class[] {iface});
}
/** Note if a connection object is monitored any Statements, PreparedStatements, CallableStatements, and
* optionally ResultSets that it creates will automatically be monitored. So for jdbc interfaces usually it will be sufficient
* to simply monitor the connection. You could also call MonProxyFactory.monitor((Object)conn);
* and the connection would be monitored however other child objects wouldn't be and recently executed sql would not be put
* in a buffer. The same applies to the other overloaded sql monitor(...) method calls below. For sql monitored objects
* to be monitored both the overall monitoring must be enabled. Monitoring rules apply as discussed in the top of this document.
*
*/
public Connection monitor(Connection conn) {
return (Connection) monitorJDBC(conn);
}
/** Monitor a resultSets methods. Note the version that takes an explicit class is used for when the class is a proxy*/
public ResultSet monitor(ResultSet rs) {
return (ResultSet) monitorJDBC(rs);
}
/** Monitor a Statements methods, as well as any ResultSets it returns (assuming the proper monitoring options are enabled) */
public Statement monitor(Statement statement) {
return (Statement) monitorJDBC(statement);
}
/** Monitor a PreparedStatements methods, as well as any ResultSets it returns (assuming the proper monitoring options are enabled) */
public PreparedStatement monitor(PreparedStatement statement) {
return (PreparedStatement) monitorJDBC(statement);
}
/** Monitor a CallableStatements methods, as well as any ResultSets it returns (assuming the proper monitoring options are enabled) */
public CallableStatement monitor(CallableStatement statement) {
return (CallableStatement) monitorJDBC(statement);
}
Object monitorJDBC(Object object) {
// if monitoring is not enabled, sql monitoring is not enabled, the object is a null or already an instance of a proxy then return
// the original object unchanged. (it would have to be a proxy object with a JDBCMonProxy invocation handler
if (!params.isEnabled || (!params.isSQLSummaryEnabled && !params.isSQLDetailEnabled) || object==null || (object instanceof Proxy && Proxy.getInvocationHandler(object) instanceof JDBCMonProxy)) // if not enabled return the original object unchanged, not the proxy
return object;
else {
MonProxy monProxy=new JDBCMonProxy(object, params, (MonProxyLabelerInt) labelFactory.clone());
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
getInterfaces(object.getClass()),// proxy will implement passed in interfaces
monProxy
);
}
}
/** For every class in the Object/Interface heirarchy find its implemented interfaces. All interfaces this class
* implements are returned. Either the Class of an Object or interfaces Class may be passed to
* this method. The difference between this method and the method 'Class.getInterfaces()' is
* that this one returns ALL implemented interfaces whereas that one only returns interfaces that
* are directly implemented by the Class.
*/
public Class[] getInterfaces(Class cls) {
if (cls==null)
return null;
Set interfaceHeirarchy=new HashSet();
// Get class heirarchy and loop through it and its interfaces adding each interface to the passed
// in Set.
Class[] objTree=getClassHeirarchy(cls);
for (int i=0;i