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); } } }