package sk.stuba.fiit.perconik.activity.probes;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import sk.stuba.fiit.perconik.data.content.AnyContent;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableMap.copyOf;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Maps.filterEntries;
import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly;
import static sk.stuba.fiit.perconik.utilities.MoreThrowables.initializeSuppressor;
public final class Probers {
private Probers() {}
private static final class RegularProber<T extends AnyContent, P extends Probe<?>> extends AbstractProber<T, P> {
final ImmutableMap<String, P> probes;
RegularProber(final Map<String, P> probes) {
this.probes = copyOf(probes);
}
public final Map<String, P> probes() {
return this.probes;
}
}
private static final class FilteringProber<T extends AnyContent, P extends Probe<?>> extends AbstractProber<T, P> {
final ImmutableMap<String, P> probes;
final Predicate<? super Entry<String, P>> predicate;
FilteringProber(final Map<String, P> probes, final Predicate<? super Entry<String, P>> predicate) {
this.probes = copyOf(probes);
this.predicate = checkNotNull(predicate);
}
public final Map<String, P> probes() {
return filterEntries(this.probes, this.predicate);
}
@Override
protected ToStringHelper toStringHelper() {
ToStringHelper helper = super.toStringHelper();
helper.add("predicate", this.predicate);
return helper;
}
}
private static abstract class ProberProxy<T extends AnyContent, P extends Probe<?>> extends AbstractProber<T, P> {
final Prober<T, P> prober;
public ProberProxy(final Prober<T, P> prober) {
this.prober = checkNotNull(prober);
}
public final Map<String, P> probes() {
return this.prober.probes();
}
@Override
protected ToStringHelper toStringHelper() {
ToStringHelper helper = MoreObjects.toStringHelper(this);
helper.add("proxy", this.prober);
return helper;
}
}
private static final class SameThreadProber<T extends AnyContent, P extends Probe<?>> extends ProberProxy<T, P> {
SameThreadProber(final Prober<T, P> prober) {
super(prober);
}
}
private static final class ConcurrentProber<T extends AnyContent, P extends Probe<?>> extends ProberProxy<T, P> {
final Executor executor;
ConcurrentProber(final Prober<T, P> prober, final Executor executor) {
super(prober);
this.executor = checkNotNull(executor);
}
@Override
public void inject(final T content) {
final Map<String, P> probes = copyOf(this.prober.probes());
final CountDownLatch latch = new CountDownLatch(probes.size());
final List<RuntimeException> failures = newLinkedList();
for (final Entry<String, P> entry: probes.entrySet()) {
this.executor.execute(new Runnable() {
public void run() {
try {
probeAndPut(entry.getValue(), entry.getKey(), content);
} catch (ProbingException failure) {
failures.add(failure);
} finally {
latch.countDown();
}
}
});
}
awaitUninterruptibly(latch);
if (!failures.isEmpty()) {
throw initializeSuppressor(new ProbingException(), failures);
}
}
@Override
protected ToStringHelper toStringHelper() {
ToStringHelper helper = super.toStringHelper();
helper.add("executor", this.executor);
return helper;
}
}
public static <T extends AnyContent, P extends Probe<?>> Prober<T, P> create(final Map<String, P> probes) {
return new SameThreadProber<>(new RegularProber<T, P>(probes));
}
public static <T extends AnyContent, P extends Probe<?>> Prober<T, P> create(final Map<String, P> probes, final Executor executor) {
return new ConcurrentProber<>(new RegularProber<T, P>(probes), executor);
}
public static <T extends AnyContent, P extends Probe<?>> Prober<T, P> create(final Map<String, P> probes, final Predicate<? super Entry<String, P>> predicate) {
return new SameThreadProber<>(new FilteringProber<T, P>(probes, predicate));
}
public static <T extends AnyContent, P extends Probe<?>> Prober<T, P> create(final Map<String, P> probes, final Predicate<? super Entry<String, P>> predicate, final Executor executor) {
return new ConcurrentProber<>(new FilteringProber<T, P>(probes, predicate), executor);
}
}