001: package com.jamonapi.proxy;
002:
003: import java.lang.reflect.*;
004: import java.sql.SQLException;
005: import java.util.*;
006:
007: import com.jamonapi.Monitor;
008: import com.jamonapi.MonitorFactory;
009: import com.jamonapi.MonKeyImp;
010: import com.jamonapi.utils.*;
011:
012: /**
013: * By using this proxy class ANY java interface can be monitored for performance and exceptions via JAMon.
014: * In particular the 'monitor' method is great for wrapping (and so monitoring) any of the JDBC classes which
015: * all implement interfaces (i.e. Connection, ResultSet, Statement, PreparedStatement,...). It tracks performance
016: * stats (hits/avg/min/max/...) for method calls and Exceptions.
017: *
018: * Sample code:
019: * ResultSet rs= MonProxyFactory.monitor(resultSet);
020: * Connection conn=MonProxyFactory.monitor(connection);
021: * MyInterface my=(MyInterface) MonProxyFactory.monitor(myObject);//myObject implements MyInterface
022: * YourInterface your=(YourInterface) MonProxyFactory.monitor(yourObject);//myObject implements MyInterface
023: *
024: * Subsequent public method calls on this interface will be monitored. Quite cool. If the proxy is disabled
025: * the Object will be returned unchanged.
026: *
027: * Note the object passed MUST implement an interface or a runtime exception will occur i..e it can't be a
028: * plain java object.
029: *
030: *
031: *
032: */
033: public class MonProxy implements InvocationHandler {
034:
035: private Object monitoredObj;// underlying object
036:
037: private String className;// class name of monitored object
038:
039: Params params;// parameters associated with monitoring
040:
041: private static Method EQUALS_METHOD;
042: static {
043:
044: try {
045: EQUALS_METHOD = Object.class.getMethod("equals",
046: new Class[] { Object.class });
047: } catch (Exception e) {
048: Logger
049: .log("Error trying to create reflective equals method. This error should never happen: "
050: + e);
051: }
052:
053: }
054:
055: MonProxy(Object monitoredObj, Params params) {
056: this .monitoredObj = monitoredObj;
057: this .params = params;
058: this .className = "(class=" + monitoredObj.getClass().getName()
059: + ")";
060: }
061:
062: /** Return the underlying object being Monitored. If the monitored object is wrapped with another Monitored object
063: * it will call until MonProxy is not returned (i.e. find the underlying monitored object */
064: public Object getMonitoredObject() {
065: return getMonitoredObject(monitoredObj);
066: }
067:
068: /** Static method that returns the underlying/wrapped/monitored object if the passed object is an interfaced monitored by JAMon else
069: * if not simply return the object unchanged. It will follow the chain of MonProxy objects until
070: * the first non-MonProxy object is returned. This could return another type of Proxy object too.
071: * */
072: public static Object getMonitoredObject(Object obj) {
073:
074: if (obj == null)
075: return null;
076:
077: // loop until the monitored object is not a MonProxy object.
078: // note this is a recursive call.
079: while (Proxy.isProxyClass(obj.getClass())
080: && (Proxy.getInvocationHandler(obj) instanceof MonProxy)) {
081: MonProxy monProxy = (MonProxy) Proxy
082: .getInvocationHandler(obj);
083: obj = monProxy.getMonitoredObject();
084: }
085:
086: return obj;
087:
088: }
089:
090: /** Method that monitors method invocations of the proxied interface. This method is not explicitly called.
091: * The MonProxy class automatically calls it.
092: *
093: *
094: */
095: public Object invoke(Object proxy, Method method, Object[] args)
096: throws Throwable {
097:
098: Monitor mon = null;
099: boolean isExceptionSummaryEnabled = (params.isExceptionSummaryEnabled && params.isEnabled);// track jamon stats for Exceptions?
100: boolean isExceptionDetailEnabled = (params.isExceptionDetailEnabled && params.isEnabled);// save detailed stack trace in the exception buffer?
101: // Because this monitor string is created every time I use a StringBuffer as it may be more effecient.
102: // I didn't do this in the exception part of the code as they shouldn't be called as often.
103: if (params.isInterfaceEnabled && params.isEnabled)
104: mon = MonitorFactory.start(new StringBuffer().append(
105: "MonProxy-Interface ").append(className).append(
106: ": ").append(method.toString()).toString());
107:
108: try {
109: // Special logic must be performed for 'equals'. If not nonproxy.equals(proxy)
110: // will not return true
111: if (method.equals(EQUALS_METHOD))
112: return Boolean.valueOf(equals(args[0]));
113: else
114: return method.invoke(monitoredObj, args);// executes underlying interfaces method;
115: } catch (InvocationTargetException e) {
116:
117: if (isExceptionSummaryEnabled || isExceptionDetailEnabled) {
118: String sqlMessage = "";
119: Throwable rootCause = e.getCause();
120:
121: // Add special info if it is a SQLException
122: if (rootCause instanceof SQLException
123: && isExceptionSummaryEnabled) {
124: SQLException sqlException = (SQLException) rootCause;
125: sqlMessage = ",ErrorCode="
126: + sqlException.getErrorCode()
127: + ",SQLState=" + sqlException.getSQLState();
128: }
129:
130: if (isExceptionSummaryEnabled) {
131: // Add jamon entries for Exceptions
132: String detailStackTrace = Misc
133: .getExceptionTrace(rootCause);
134:
135: MonitorFactory.add(new MonKeyImp(
136: MonitorFactory.EXCEPTIONS_LABEL,
137: detailStackTrace, "Exception"), 1); // counts total exceptions from jamon
138: MonitorFactory
139: .add(
140: new MonKeyImp(
141: "MonProxy-Exception: InvocationTargetException",
142: detailStackTrace,
143: "Exception"), 1); //counts total exceptions for MonProxy
144: MonitorFactory.add(new MonKeyImp(
145: "MonProxy-Exception: Root cause exception="
146: + rootCause.getClass().getName()
147: + sqlMessage, detailStackTrace,
148: "Exception"), 1); // Message for the exception
149: MonitorFactory.add(new MonKeyImp(
150: "MonProxy-Exception: " + className
151: + " Exception: "
152: + method.toString(),
153: detailStackTrace, "Exception"), 1); // Exception and method that threw it.
154:
155: }
156:
157: // Add stack trace to buffer if it is enabled.
158: if (isExceptionDetailEnabled)
159: params.exceptionBuffer.addRow(new Object[] {
160: new Long(++params.exceptionID), new Date(),
161: Misc.getExceptionTrace(rootCause),
162: method.toString(), });
163: } // end if (enabled)
164:
165: throw e.getCause();
166:
167: } finally {
168: if (mon != null)
169: mon.stop();
170: }
171: }
172:
173: // private boolean equals(Object proxy, Object obj) {
174: // return equals(getMonitoredObject(obj));
175: // }
176:
177: /** When this is called on the proxy object it is the same as calling
178: * proxyObject1.equals(proxyObject2) is the same as calling originalObject1.equals(originalObject2)
179: *
180: */
181: public boolean equals(Object obj) {
182: return getMonitoredObject().equals(getMonitoredObject(obj));
183: }
184:
185: }
|