package se.l4.vibe.probes; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; import se.l4.vibe.probes.Sampler.Entry; /** * General probes that work with {@link Sampler sampler}. * * @author Andreas Holstenson * */ public class SamplerProbes { private SamplerProbes() { } /** * Create a probe for the specified sampler that will keep track of values * over the specified time. * * @param series * @param duration * @param unit * @param operation * @return */ public static <Input, Output> Probe<Output> forSampler( Sampler<Input> series, long duration, TimeUnit unit, SampleOperation<Input, Output> operation ) { return new TimeLimitedProbe<Input, Output>(duration, unit, series, operation); } /** * Create a probe for the specified series that will keep track of values * over the specified time. * * @param series * @param duration * @param unit * @return */ public static <T extends ModifiableData<T>> Probe<T> forSampler( Sampler<T> series, long duration, TimeUnit unit ) { return forSampler(series, duration, unit, new ModifiableDataOperation<T>()); } /** * Create a probe for the specified series that keep track of values * ever sampled. * * <p> * The given operation will <b>not</b> have access to the entire range of * {@link Sampler.Entry entries} in the * {@link SampleOperation#add(Object, java.util.Collection) add method}. * * @param series * @param operation * @return */ public static <Input, Output> Probe<Output> forSampler( Sampler<Input> series, SampleOperation<Input, Output> operation ) { return new EternityProbe<Input, Output>(series, operation); } /** * Create a probe for the specified series that keep track of values * ever sampled. * * <p> * This is can be used if the data stored in the series implements * {@link ModifiableData}. * * @param series * @return */ public static <T extends ModifiableData<T>> Probe<T> forSampler(Sampler<T> series) { return forSampler(series, new ModifiableDataOperation<T>()); } private static class EternityProbe<Input, Output> implements Probe<Output> { private final SampleOperation<Input, Output> operation; public EternityProbe(Sampler<Input> series, SampleOperation<Input, Output> operation) { this.operation = operation; series.addListener(new SampleListener<Input>() { @Override public void sampleAcquired(SampledProbe<Input> probe, Sampler.Entry<Input> value) { handleSampleAcquired(value); } }); } protected void handleSampleAcquired(Entry<Input> value) { operation.add(value.getValue(), null); } @Override public Output read() { return operation.get(); } } /** * Probe that is time limited based on the input of a * {@link Sampler}. * * @author Andreas Holstenson * */ private static class TimeLimitedProbe<Input, Output> implements Probe<Output> { private final SampleOperation<Input, Output> operation; private final long maxAge; private final List<Sampler.Entry<Input>> entries; /** * Create a new time limited probe that will be limited to the specified * time. The operation is used to calculate the value of the probe. * * @param time * @param unit * @param operation */ public TimeLimitedProbe( long time, TimeUnit unit, Sampler<Input> series, SampleOperation<Input, Output> operation) { maxAge = unit.toMillis(time); this.operation = operation; entries = new LinkedList<Sampler.Entry<Input>>(); series.addListener(new SampleListener<Input>() { @Override public void sampleAcquired(SampledProbe<Input> probe, Sampler.Entry<Input> value) { handleSampleAcquired(value); } }); } private void handleSampleAcquired(Sampler.Entry<Input> entry) { if(! entries.isEmpty()) { /* * If we have entries check if the first one should be * removed or kept. */ Sampler.Entry<Input> firstEntry = entries.get(0); if(firstEntry.getTime() < System.currentTimeMillis() - maxAge) { entries.remove(0); operation.remove(firstEntry.getValue(), entries); } } entries.add(entry); operation.add(entry.getValue(), entries); } @Override public Output read() { return operation.get(); } } private static class ModifiableDataOperation<T extends ModifiableData<T>> implements SampleOperation<T, T> { private T data; @Override public void add(T value, Collection<Entry<T>> entries) { if(data == null) { data = value; return; } data = data.add(value); } @Override public void remove(T value, Collection<Entry<T>> entries) { if(data == null) return; data = data.remove(value); } @Override public T get() { return data; } } }