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    }