package org.gridkit.jvmtool.event;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This utility class creates dynamic wrapper over {@link Event}
* and replaces tags and counter with mutable versions;
*
* @author Alexey Ragozin (alexey.ragozin@gmail.com)
*/
public class EventDecorator {
private long timestamp = -1;
private SimpleTagCollection tags = new SimpleTagCollection();
private SimpleCounterCollection counters = new SimpleCounterCollection();
private Map<Class<?>, Event> wrapperCache = new HashMap<Class<?>, Event>();
private InvocationHandler handler = new Handler();
private Event delegate;
public EventDecorator() {
}
@SuppressWarnings("unchecked")
public <T extends Event> T wrap(T event) {
if (event == null) {
return null;
}
timestamp = -1;
tags.clear();
counters.clear();
delegate = event;
if (event instanceof TimestampedEvent) {
timestamp = ((TimestampedEvent) event).timestamp();
}
if (event instanceof MultiCounterEvent) {
counters.setAll(((MultiCounterEvent) event).counters());
}
if (event instanceof TaggedEvent) {
tags.putAll(((TaggedEvent) event).tags());
}
Event proxy = wrapperCache.get(event.getClass());
if (proxy == null) {
initProxy(event.getClass());
proxy = wrapperCache.get(event.getClass());
}
return (T)proxy;
}
private void initProxy(Class<? extends Event> type) {
List<Class<?>> facade = new ArrayList<Class<?>>();
for(Class<?> c: type.getInterfaces()) {
if (Event.class.isAssignableFrom(c)) {
facade.add(c);
}
}
if (facade.isEmpty()) {
throw new IllegalArgumentException("Invalid event type: " + type.getClass());
}
opt:
while(true) {
for(Class<?> i: facade) {
for(Class<?> j: facade) {
if (i != j && i.isAssignableFrom(j)) {
facade.remove(i);
continue opt;
}
}
}
break;
}
Object proxy = Proxy.newProxyInstance(facade.get(0).getClassLoader(), facade.toArray(new Class<?>[0]), handler);
wrapperCache.put(type, (Event)proxy);
}
public void timestamp(long timestamp) {
this.timestamp = timestamp;
}
public SimpleTagCollection tags() {
return this.tags;
}
public SimpleCounterCollection counters() {
return this.counters;
}
private class Handler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (args == null || args.length == 0) {
if ("timestamp".equals(method.getName())) {
return timestamp;
}
else if ("tags".equals(method.getName())) {
return tags;
}
else if ("counters".equals(method.getName())) {
return counters;
}
}
return method.invoke(delegate, args);
}
}
}