001: /*
002: * MCS Media Computer Software Copyright (c) 2006 by MCS
003: * -------------------------------------- Created on 26.07.2006 by W.Klaas
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
006: * use this file except in compliance with the License. You may obtain a copy of
007: * the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017: package de.mcs.jmeasurement.proxy;
018:
019: import java.lang.reflect.InvocationHandler;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.lang.reflect.UndeclaredThrowableException;
023: import java.util.HashSet;
024: import java.util.Iterator;
025:
026: import de.mcs.jmeasurement.MeasureFactory;
027: import de.mcs.jmeasurement.Monitor;
028: import de.mcs.utils.StringFormat;
029:
030: /**
031: * This is my monitor class for monitoring interface methodes automatically. The
032: * name of the measurepoints will be automatically generated as
033: *
034: * <pre>
035: * [fullclassname]#[methodename]
036: * </pre>
037: *
038: * @since 0.64
039: * @author W.Klaas
040: *
041: */
042: public class ProxyMonitor implements InvocationHandler {
043:
044: /** the interface under invocation monitoring. */
045: private Object testObject;
046:
047: /** classname as string for direct performant access. */
048: private String className;
049:
050: /** exceptions should be stored. */
051: private boolean storeExceptions;
052:
053: /** full exceptions with stacktraces. */
054: private boolean fullExceptions;
055:
056: /** set with all method names. */
057: private HashSet<MethodName> methodNames;
058:
059: /** using the default exception handling. */
060: private boolean defaultExceptionHandling;
061:
062: private class MethodName {
063:
064: private boolean bParameterExtension;
065:
066: private String name;
067:
068: private int[] indexes;
069:
070: /**
071: * @param aName
072: */
073: public MethodName(final String aName) {
074: bParameterExtension = false;
075: this .name = aName;
076: if (aName.indexOf(".") > 0) {
077: bParameterExtension = true;
078: String argument = aName
079: .substring(aName.indexOf(".") + 1);
080: String[] arguments = argument.split("\\.");
081: indexes = new int[arguments.length];
082: for (int i = 0; i < arguments.length; i++) {
083: String indexStr = arguments[i].substring(3);
084: indexes[i] = Integer.parseInt(indexStr);
085: }
086: this .name = aName.substring(0, aName.indexOf("."));
087: }
088: }
089:
090: /**
091: * @return the bParameterExtension
092: */
093: public boolean isParameterExtension() {
094: return bParameterExtension;
095: }
096:
097: /**
098: * @return the name
099: */
100: public String getName() {
101: return name;
102: }
103:
104: public String getParameterPart(final Object[] arguments) {
105: StringBuffer part = new StringBuffer();
106: for (int i = 0; i < indexes.length; i++) {
107: part.append(arguments[indexes[i]]);
108: part.append(".");
109: }
110: return part.substring(0, part.length() - 1);
111: }
112: }
113:
114: /**
115: * constructor for this proxy.
116: *
117: * @param aTestObject
118: * object under test.
119: * @param aStoreExceptions
120: * if exceptions should be stored.
121: * @param aFullExceptions
122: * full exceptions with stacktraces
123: * @param aMethodNames
124: * list with all methodnames that should be monitored. If this
125: * parameter is null all methods will be monitored.
126: */
127: public ProxyMonitor(final Object aTestObject,
128: final boolean aStoreExceptions,
129: final boolean aFullExceptions, final String[] aMethodNames) {
130: super ();
131: this .testObject = aTestObject;
132: className = testObject.getClass().getName() + '#';
133: this .defaultExceptionHandling = false;
134: this .storeExceptions = aStoreExceptions;
135: this .fullExceptions = aFullExceptions;
136: methodNames = new HashSet<MethodName>();
137: if (aMethodNames != null) {
138: for (int i = 0; i < aMethodNames.length; i++) {
139: methodNames.add(new MethodName(aMethodNames[i]));
140: }
141: }
142: }
143:
144: /**
145: * constructor for this proxy. Using default exception handling.
146: *
147: * @param aTestObject
148: * object under test.
149: * @param aMethodNames
150: * list with all methodnames that should be monitored. If this
151: * parameter is null all methods will be monitored.
152: */
153: public ProxyMonitor(final Object aTestObject,
154: final String[] aMethodNames) {
155: super ();
156: this .testObject = aTestObject;
157: className = testObject.getClass().getName() + '#';
158: methodNames = new HashSet<MethodName>();
159: this .defaultExceptionHandling = true;
160: if (aMethodNames != null) {
161: for (int i = 0; i < aMethodNames.length; i++) {
162: MethodName methodName = new MethodName(aMethodNames[i]);
163: methodNames.add(methodName);
164: }
165: }
166: }
167:
168: /**
169: * This is the methode that will alway be inveked on every methode
170: * invocation of the object under test. Processes a method invocation on a
171: * proxy instance and returns the result. This method will be invoked on an
172: * invocation handler when a method is invoked on a proxy instance that it
173: * is associated with.
174: *
175: * @param proxy
176: * the proxy instance that the method was invoked on
177: *
178: * @param method
179: * the <code>Method</code> instance corresponding to the
180: * interface method invoked on the proxy instance. The declaring
181: * class of the <code>Method</code> object will be the
182: * interface that the method was declared in, which may be a
183: * superinterface of the proxy interface that the proxy class
184: * inherits the method through.
185: *
186: * @param args
187: * an array of objects containing the values of the arguments
188: * passed in the method invocation on the proxy instance, or
189: * <code>null</code> if interface method takes no arguments.
190: * Arguments of primitive types are wrapped in instances of the
191: * appropriate primitive wrapper class, such as
192: * <code>java.lang.Integer</code> or
193: * <code>java.lang.Boolean</code>.
194: *
195: * @return the value to return from the method invocation on the proxy
196: * instance. If the declared return type of the interface method is
197: * a primitive type, then the value returned by this method must be
198: * an instance of the corresponding primitive wrapper class;
199: * otherwise, it must be a type assignable to the declared return
200: * type. If the value returned by this method is <code>null</code>
201: * and the interface method's return type is primitive, then a
202: * <code>NullPointerException</code> will be thrown by the method
203: * invocation on the proxy instance. If the value returned by this
204: * method is otherwise not compatible with the interface method's
205: * declared return type as described above, a
206: * <code>ClassCastException</code> will be thrown by the method
207: * invocation on the proxy instance.
208: *
209: * @throws Throwable
210: * the exception to throw from the method invocation on the
211: * proxy instance. The exception's type must be assignable
212: * either to any of the exception types declared in the
213: * <code>throws</code> clause of the interface method or to
214: * the unchecked exception types
215: * <code>java.lang.RuntimeException</code> or
216: * <code>java.lang.Error</code>. If a checked exception is
217: * thrown by this method that is not assignable to any of the
218: * exception types declared in the <code>throws</code> clause
219: * of the interface method, then an
220: * {@link UndeclaredThrowableException} containing the exception
221: * that was thrown by this method will be thrown by the method
222: * invocation on the proxy instance.
223: *
224: * @see java.lang.refelct.InvocationHandler#invoke(java.lang.Object,
225: * java.lang.reflect.Method, java.lang.Object[])
226: */
227: public final Object invoke(final Object proxy, final Method method,
228: final Object[] args) throws Throwable {
229: Object result = null;
230: MethodName methodName = getMethodName(method.getName());
231: if (methodNames.isEmpty() || (methodName != null)) {
232: StringBuffer classMethodName = new StringBuffer();
233: classMethodName.append(className);
234: classMethodName.append(method.getName());
235: if ((methodName != null)
236: && methodName.isParameterExtension()) {
237: classMethodName.append(".");
238: classMethodName.append(methodName
239: .getParameterPart(args));
240: }
241: Monitor monitor = MeasureFactory.start(classMethodName
242: .toString());
243: try {
244: result = method.invoke(testObject, args);
245: monitor.stop();
246: } catch (InvocationTargetException e) {
247: String stackTrace = "";
248: if (!defaultExceptionHandling) {
249: if (storeExceptions) {
250: if (fullExceptions) {
251: stackTrace = StringFormat.getStackTrace(e
252: .getCause());
253: } else {
254: stackTrace = e.getCause().toString();
255: }
256: }
257: monitor.setException(stackTrace);
258: } else {
259: monitor.setException(e.getCause());
260: }
261: throw e.getCause();
262: }
263: } else {
264: // if this methode should not be monitored
265: try {
266: result = method.invoke(testObject, args);
267: } catch (InvocationTargetException e) {
268: throw e.getCause();
269: }
270: }
271: return result;
272: }
273:
274: /**
275: * @param name
276: * @return
277: */
278: private MethodName getMethodName(String name) {
279: for (Iterator iter = methodNames.iterator(); iter.hasNext();) {
280: MethodName methodName = (MethodName) iter.next();
281: if (methodName.getName().equals(name)) {
282: return methodName;
283: }
284: }
285: return null;
286: }
287: }
|