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 015package org.apache.hivemind.management.impl; 016 017import java.lang.reflect.InvocationHandler; 018import java.lang.reflect.InvocationTargetException; 019import java.lang.reflect.Method; 020import java.lang.reflect.Proxy; 021import java.util.HashSet; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Set; 025 026import javax.management.JMException; 027import javax.management.ObjectName; 028 029import org.apache.hivemind.ApplicationRuntimeException; 030import org.apache.hivemind.InterceptorStack; 031import org.apache.hivemind.ServiceInterceptorFactory; 032import org.apache.hivemind.internal.Module; 033import org.apache.hivemind.internal.ServicePoint; 034import org.apache.hivemind.management.MBeanRegistry; 035import org.apache.hivemind.management.ManagementMessages; 036import org.apache.hivemind.management.ObjectNameBuilder; 037import org.apache.hivemind.management.mbeans.PerformanceMonitorMBean; 038import org.apache.hivemind.methodmatch.MethodMatcher; 039import org.apache.hivemind.service.MethodContribution; 040import org.apache.hivemind.service.MethodIterator; 041import 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 */ 053public 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}