blob: a0fc932732d82922fc39cf60ebd058147f000f69 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.openejb.monitoring;
import org.apache.openejb.math.stat.descriptive.SynchronizedDescriptiveStatistics;
import org.apache.xbean.finder.ClassFinder;
import org.apache.openejb.api.Monitor;
import org.apache.openejb.core.interceptor.InterceptorData;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.AfterCompletion;
import javax.ejb.BeforeCompletion;
import javax.ejb.AfterBegin;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.interceptor.AroundTimeout;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* @version $Rev$ $Date$
*/
public class StatsInterceptor {
public static final InterceptorData metadata = InterceptorData.scan(StatsInterceptor.class);
private final Map<Method, Stats> map = new ConcurrentHashMap<Method, Stats>();
private final AtomicLong invocations = new AtomicLong();
private final AtomicLong invocationTime = new AtomicLong();
private Monitor monitor;
private final boolean enabled;
public StatsInterceptor(Class<?> componentClass) {
monitor = componentClass.getAnnotation(Monitor.class);
ClassFinder finder = new ClassFinder(componentClass);
for (Method method : finder.findAnnotatedMethods(Monitor.class)) {
map.put(method, new Stats(method, monitor));
}
enabled = monitor != null || map.size() > 0;
}
public boolean isMonitoringEnabled() {
return enabled;
}
@Managed
public long getInvocationCount() {
return invocations.get();
}
@Managed
public long getInvocationTime() {
return invocationTime.get();
}
@Managed
public long getMonitoredMethods() {
return map.size();
}
@ManagedCollection(type = Stats.class, key = "method")
public Collection<Stats> stats() {
return map.values();
}
// private Method $n() throws NoSuchMethodException { return this.getClass().getMethod(\"$n\"); } @$n public void $n(InvocationContext invocationContext) throws Exception { record(invocationContext, $n()); }
@AroundInvoke
public Object invoke(InvocationContext invocationContext) throws Exception {
return record(invocationContext, null);
}
public Method PostConstruct() throws NoSuchMethodException {
return this.getClass().getMethod("PostConstruct");
}
@PostConstruct
public void PostConstruct(InvocationContext invocationContext) throws Exception {
record(invocationContext, PostConstruct());
}
public Method PreDestroy() throws NoSuchMethodException {
return this.getClass().getMethod("PreDestroy");
}
@PreDestroy
public void PreDestroy(InvocationContext invocationContext) throws Exception {
record(invocationContext, PreDestroy());
}
public Method PostActivate() throws NoSuchMethodException {
return this.getClass().getMethod("PostActivate");
}
@PostActivate
public void PostActivate(InvocationContext invocationContext) throws Exception {
record(invocationContext, PostActivate());
}
public Method PrePassivate() throws NoSuchMethodException {
return this.getClass().getMethod("PrePassivate");
}
@PrePassivate
public void PrePassivate(InvocationContext invocationContext) throws Exception {
record(invocationContext, PrePassivate());
}
public Method AroundTimeout() throws NoSuchMethodException {
return this.getClass().getMethod("AroundTimeout");
}
@AroundTimeout
public void AroundTimeout(InvocationContext invocationContext) throws Exception {
record(invocationContext, AroundTimeout());
}
public Method AfterBegin() throws NoSuchMethodException {
return this.getClass().getMethod("AfterBegin");
}
@AfterBegin
public void AfterBegin(InvocationContext invocationContext) throws Exception {
record(invocationContext, AfterBegin());
}
public Method BeforeCompletion() throws NoSuchMethodException {
return this.getClass().getMethod("BeforeCompletion");
}
@BeforeCompletion
public void BeforeCompletion(InvocationContext invocationContext) throws Exception {
record(invocationContext, BeforeCompletion());
}
public Method AfterCompletion() throws NoSuchMethodException {
return this.getClass().getMethod("AfterCompletion");
}
@AfterCompletion
public void AfterCompletion(InvocationContext invocationContext) throws Exception {
record(invocationContext, AfterCompletion());
}
private Object record(InvocationContext invocationContext, Method callback) throws Exception {
invocations.incrementAndGet();
Stats stats = enabled ? stats(invocationContext, callback): null;
long start = System.nanoTime();
try{
return invocationContext.proceed();
} finally {
long time = millis(System.nanoTime() - start);
if (stats != null) stats.record(time);
invocationTime.addAndGet(time);
}
}
private long millis(long nanos) {
return TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS);
}
private Stats stats(InvocationContext invocationContext, Method callback) {
Method method = callback == null? invocationContext.getMethod(): callback;
Stats stats = map.get(method);
if (stats == null) {
synchronized (map) {
stats = map.get(method);
if (stats == null) {
stats = new Stats(method, monitor);
map.put(method, stats);
}
}
}
return stats;
}
public class Stats {
private final AtomicLong count = new AtomicLong();
private final SynchronizedDescriptiveStatistics samples;
// Used as the prefix for the MBeanAttributeInfo
private final String method;
public Stats(Method method, Monitor classAnnotation) {
Monitor methodAnnotation = method.getAnnotation(Monitor.class);
int window = (methodAnnotation != null) ? methodAnnotation.sample() : (classAnnotation != null) ? classAnnotation.sample() : 2000;
this.samples = new SynchronizedDescriptiveStatistics(window);
String s = ",";
StringBuilder sb = new StringBuilder(method.getName());
sb.append("(");
Class<?>[] params = method.getParameterTypes();
for (Class<?> clazz : params) {
sb.append(clazz.getSimpleName());
sb.append(s);
}
if (params.length > 0) sb.delete(sb.length() - s.length(), sb.length());
sb.append(")");
this.method = sb.toString();
}
@Managed
public void setSampleSize(int i) {
samples.setWindowSize(i);
}
@Managed
public int getSampleSize() {
return samples.getWindowSize();
}
@Managed
public long getCount() {
return count.get();
}
@Managed
public double getPercentile99() {
return samples.getPercentile(99.0);
}
@Managed
public double getPercentile90() {
return samples.getPercentile(90.0);
}
@Managed
public double getPercentile75() {
return samples.getPercentile(75.0);
}
@Managed
public double getPercentile50() {
return samples.getPercentile(50.0);
}
@Managed
public double getPercentile25() {
return samples.getPercentile(25.0);
}
@Managed
public double getPercentile10() {
return samples.getPercentile(10.0);
}
@Managed
public double getPercentile01() {
return samples.getPercentile(1.0);
}
@Managed
public double getStandardDeviation() {
return samples.getStandardDeviation();
}
@Managed
public double getMean() {
return samples.getMean();
}
@Managed
public double getVariance() {
return samples.getVariance();
}
@Managed
public double getGeometricMean() {
return samples.getGeometricMean();
}
@Managed
public double getSkewness() {
return samples.getSkewness();
}
@Managed
public double getKurtosis() {
return samples.getKurtosis();
}
@Managed
public double getMax() {
return samples.getMax();
}
@Managed
public double getMin() {
return samples.getMin();
}
@Managed
public double getSum() {
return samples.getSum();
}
@Managed
public double getSumsq() {
return samples.getSumsq();
}
@Managed
public double[] sortedValues() {
return samples.getSortedValues();
}
@Managed
public double[] values() {
return samples.getValues();
}
public void record(long time) {
count.incrementAndGet();
samples.addValue(time);
}
}
}