/*
* JBoss, Home of Professional Open Source.
* Copyright (c) 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.ejb3.component.invocationmetrics;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author <a href="mailto:cdewolf@redhat.com">Carlo de Wolf</a>
*/
public class InvocationMetrics {
public static class Values {
final long invocations;
final long executionTime;
final long waitTime;
private Values(final long invocations, final long waitTime, final long executionTime) {
this.invocations = invocations;
this.executionTime = executionTime;
this.waitTime = waitTime;
}
public long getExecutionTime() {
return executionTime;
}
public long getInvocations() {
return invocations;
}
public long getWaitTime() {
return waitTime;
}
}
private final AtomicReference<Values> values = new AtomicReference<Values>(new Values(0, 0, 0));
private final AtomicLong concurrent = new AtomicLong(0);
private final AtomicLong peakConcurrent = new AtomicLong(0);
private final ConcurrentMap<String, AtomicReference<Values>> methods = new ConcurrentHashMap<String, AtomicReference<Values>>();
void finishInvocation(final Method method, final long invocationWaitTime, final long invocationExecutionTime) {
concurrent.decrementAndGet();
for(;;) {
final Values oldv = values.get();
final Values newv = new Values(oldv.invocations + 1, oldv.waitTime + invocationWaitTime, oldv.executionTime + invocationExecutionTime);
if (values.compareAndSet(oldv, newv))
break;
}
final AtomicReference<Values> methodValues = ref(methods, method.getName());
for (;;) {
final Values oldv = methodValues.get();
final Values newv = new Values(oldv.invocations + 1, oldv.waitTime + invocationWaitTime, oldv.executionTime + invocationExecutionTime);
if (methodValues.compareAndSet(oldv, newv))
break;
}
}
private static AtomicReference<Values> ref(final ConcurrentMap<String, AtomicReference<Values>> map, final String key) {
AtomicReference<Values> ref = map.get(key);
if (ref == null) {
ref = new AtomicReference<Values>(new Values(0, 0, 0));
final AtomicReference<Values> prevRef = map.putIfAbsent(key, ref);
if (prevRef != null)
ref = prevRef;
}
return ref;
}
public long getConcurrent() {
return concurrent.get();
}
public long getExecutionTime() {
return values.get().executionTime;
}
public long getInvocations() {
return values.get().invocations;
}
public Map<String, Values> getMethods() {
return new AbstractMap<String, Values>() {
@Override
public Set<Entry<String, Values>> entrySet() {
return new AbstractSet<Entry<String, Values>>() {
@Override
public Iterator<Entry<String, Values>> iterator() {
final Iterator<Entry<String, AtomicReference<Values>>> delegate = methods.entrySet().iterator();
return new Iterator<Entry<String, Values>>() {
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public Entry<String, Values> next() {
final Entry<String, AtomicReference<Values>> next = delegate.next();
return new Entry<String, Values>() {
@Override
public String getKey() {
return next.getKey();
}
@Override
public Values getValue() {
return next.getValue().get();
}
@Override
public Values setValue(final Values value) {
throw new UnsupportedOperationException("NYI");
}
};
}
@Override
public void remove() {
throw new UnsupportedOperationException("NYI");
}
};
}
@Override
public int size() {
return methods.size();
}
};
}
};
}
public long getPeakConcurrent() {
return peakConcurrent.get();
}
public long getWaitTime() {
return values.get().waitTime;
}
void startInvocation() {
final long v = concurrent.incrementAndGet();
// concurrent might decrement here, but we take that missing peak for granted.
if (peakConcurrent.get() < v)
peakConcurrent.incrementAndGet();
}
}