001 // Copyright 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.management.impl;
016
017 import java.lang.reflect.InvocationHandler;
018 import java.lang.reflect.InvocationTargetException;
019 import java.lang.reflect.Method;
020 import java.lang.reflect.Proxy;
021 import java.util.HashSet;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Set;
025
026 import javax.management.JMException;
027 import javax.management.ObjectName;
028
029 import org.apache.hivemind.ApplicationRuntimeException;
030 import org.apache.hivemind.InterceptorStack;
031 import org.apache.hivemind.ServiceInterceptorFactory;
032 import org.apache.hivemind.internal.Module;
033 import org.apache.hivemind.internal.ServicePoint;
034 import org.apache.hivemind.management.MBeanRegistry;
035 import org.apache.hivemind.management.ManagementMessages;
036 import org.apache.hivemind.management.ObjectNameBuilder;
037 import org.apache.hivemind.management.mbeans.PerformanceMonitorMBean;
038 import org.apache.hivemind.methodmatch.MethodMatcher;
039 import org.apache.hivemind.service.MethodContribution;
040 import org.apache.hivemind.service.MethodIterator;
041 import org.apache.hivemind.service.MethodSignature;
042
043 /**
044 * Interceptor factory that adds a MBean based performance monitor to a service. The interceptor
045 * collects the number of calls, and the duration for each intercepted method. The results are
046 * delegated to an {@link org.apache.hivemind.management.mbeans.PerformanceMonitorMBean MBean} that
047 * is created and registered in the MBeanServer. Which methods are intercepted can be defined like
048 * in the logging interceptor
049 *
050 * @author Achim Huegen
051 * @since 1.1
052 */
053 public class PerformanceMonitorFactory implements ServiceInterceptorFactory
054 {
055 private static final String SERVICE_DECORATOR_TYPE = "PerformanceCollector";
056
057 private MBeanRegistry _mbeanRegistry;
058
059 private ObjectNameBuilder _objectNameBuilder;
060
061 private String _serviceId;
062
063 public PerformanceMonitorFactory(MBeanRegistry mbeanRegistry,
064 ObjectNameBuilder objectNameBuilder)
065 {
066 _mbeanRegistry = mbeanRegistry;
067 _objectNameBuilder = objectNameBuilder;
068 }
069
070 public void setServiceId(String string)
071 {
072 _serviceId = string;
073 }
074
075 /**
076 * Dynamic Proxy that counts method calls
077 */
078 private class PerformanceMonitorHandler implements InvocationHandler
079 {
080 private Object _inner;
081
082 private PerformanceCollector _counter;
083
084 private Set _interceptedMethods;
085
086 PerformanceMonitorHandler(Object inner, PerformanceCollector counter, Set interceptedMethods)
087 {
088 _inner = inner;
089 _counter = counter;
090 _interceptedMethods = interceptedMethods;
091 }
092
093 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
094 {
095 try
096 {
097 // Filter the method
098 MethodSignature signature = new MethodSignature(method);
099 if (_interceptedMethods.contains(signature))
100 {
101 // clock the execution time
102 long startTime = System.currentTimeMillis();
103 Object result = method.invoke(_inner, args);
104 long endTime = System.currentTimeMillis();
105
106 _counter.addMeasurement(signature, endTime - startTime);
107 return result;
108 }
109
110 return method.invoke(_inner, args);
111 }
112 catch (InvocationTargetException ex)
113 {
114 throw ex.getTargetException();
115 }
116 }
117
118 }
119
120 public void createInterceptor(InterceptorStack stack, Module invokingModule, List parameters)
121 {
122 ServicePoint servicePoint = invokingModule.getServicePoint(stack
123 .getServiceExtensionPointId());
124 Set methods = getInterceptedMethods(stack, parameters);
125 try
126 {
127 PerformanceCollector counter = createMBean(servicePoint, methods);
128 InvocationHandler countHandler = new PerformanceMonitorHandler(stack.peek(), counter,
129 methods);
130
131 Object proxy = Proxy.newProxyInstance(invokingModule.getClassResolver()
132 .getClassLoader(), new Class[]
133 { stack.getServiceInterface() }, countHandler);
134
135 stack.push(proxy);
136 }
137 catch (Exception ex)
138 {
139 throw new ApplicationRuntimeException(ManagementMessages
140 .errorInstantiatingPerformanceInterceptor(_serviceId, stack, ex), ex);
141 }
142 }
143
144 /**
145 * Creates and registers the MBean that holds the performance data.
146 */
147 public PerformanceCollector createMBean(ServicePoint servicePoint, Set methods)
148 throws JMException
149 {
150 PerformanceCollector counter = new PerformanceMonitorMBean(methods);
151 ObjectName objectName = _objectNameBuilder.createServiceDecoratorName(
152 servicePoint,
153 SERVICE_DECORATOR_TYPE);
154 _mbeanRegistry.registerMBean(counter, null, objectName);
155
156 return counter;
157 }
158
159 /**
160 * Creates a method matcher that helps finding the intercepted methods
161 */
162 private MethodMatcher buildMethodMatcher(List parameters)
163 {
164 MethodMatcher result = null;
165
166 Iterator i = parameters.iterator();
167 while (i.hasNext())
168 {
169 MethodContribution mc = (MethodContribution) i.next();
170
171 if (result == null)
172 result = new MethodMatcher();
173
174 result.put(mc.getMethodPattern(), mc);
175 }
176
177 return result;
178 }
179
180 /**
181 * Returns the methods that must be intercepted. Which methods are intercepted is controled by
182 * the interceptor parameters via include and exclude mechanism
183 */
184 protected Set getInterceptedMethods(InterceptorStack stack, List parameters)
185 {
186 Set methods = new HashSet();
187 MethodMatcher matcher = buildMethodMatcher(parameters);
188
189 MethodIterator mi = new MethodIterator(stack.getServiceInterface());
190
191 while (mi.hasNext())
192 {
193 MethodSignature sig = mi.next();
194
195 if (includeMethod(matcher, sig))
196 methods.add(sig);
197 }
198 return methods;
199 }
200
201 private boolean includeMethod(MethodMatcher matcher, MethodSignature sig)
202 {
203 if (matcher == null)
204 return true;
205
206 MethodContribution mc = (MethodContribution) matcher.get(sig);
207 return mc == null || mc.getInclude();
208 }
209
210 }