package edu.uw.zookeeper.client;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import edu.uw.zookeeper.EnsembleView;
import edu.uw.zookeeper.ServerInetAddressView;
import edu.uw.zookeeper.common.DefaultsFactory;
import edu.uw.zookeeper.common.ParameterizedFactory;
import edu.uw.zookeeper.common.TimeValue;
import edu.uw.zookeeper.net.ClientConnectionFactory;
import edu.uw.zookeeper.protocol.Message;
import edu.uw.zookeeper.protocol.Operation;
import edu.uw.zookeeper.protocol.ProtocolConnection;
import edu.uw.zookeeper.protocol.Session;
import edu.uw.zookeeper.protocol.client.OperationClientExecutor;
public class EnsembleViewFactory<T> implements DefaultsFactory<ServerInetAddressView, T> {
public static <C extends ProtocolConnection<? super Message.ClientSession, ? extends Operation.Response,?,?,?>> EnsembleViewFactory<ServerViewFactory<Session, OperationClientExecutor<C>>> fromSession(
ClientConnectionFactory<C> connections,
EnsembleView<ServerInetAddressView> view,
TimeValue timeOut,
ScheduledExecutorService executor) {
return random(
view,
ServerViewFactories.<C>newInstance(connections, timeOut, executor));
}
public static <T> EnsembleViewFactory<T> random(
EnsembleView<ServerInetAddressView> view,
ParameterizedFactory<ServerInetAddressView, T> factory) {
return newInstance(
view,
RandomSelector.<ServerInetAddressView>newInstance(),
factory);
}
public static <T> EnsembleViewFactory<T> newInstance(
EnsembleView<ServerInetAddressView> view,
Function<ServerInetAddressView[], ServerInetAddressView> selector,
ParameterizedFactory<ServerInetAddressView, T> factory) {
return new EnsembleViewFactory<T>(
view, selector,
InstanceFactory.newInstance(factory));
}
public static class RandomSelector<T> implements Function<T[], T> {
public static <T> RandomSelector<T> newInstance() {
return new RandomSelector<T>();
}
protected final Random random;
public RandomSelector() {
random = new Random();
}
@Override
@Nullable
public T apply(T[] input) {
return (input.length == 0) ? null : input[random.nextInt(input.length)];
}
}
public static class InstanceFactory<V,T> implements ParameterizedFactory<V,T> {
public static <V,T> InstanceFactory<V, T> newInstance(
ParameterizedFactory<V,T> factory) {
return new InstanceFactory<V,T>(factory,
Maps.<V,T>newHashMap());
}
protected final ParameterizedFactory<V, T> factory;
protected final Map<V, T> instances;
protected InstanceFactory(
ParameterizedFactory<V, T> factory,
Map<V, T> instances) {
this.factory = factory;
this.instances = instances;
}
@Override
public synchronized T get(V value) {
T instance = instances.get(value);
if (instance == null) {
instance = factory.get(value);
instances.put(value, instance);
}
return instance;
}
}
public static class ServerViewFactories<C extends ProtocolConnection<? super Message.ClientSession, ? extends Operation.Response,?,?,?>> implements ParameterizedFactory<ServerInetAddressView, ServerViewFactory<Session, OperationClientExecutor<C>>> {
public static <C extends ProtocolConnection<? super Message.ClientSession, ? extends Operation.Response,?,?,?>> ServerViewFactories<C> newInstance(
ClientConnectionFactory<C> connections,
TimeValue timeOut,
ScheduledExecutorService executor) {
return new ServerViewFactories<C>(connections, timeOut, executor);
}
protected final ClientConnectionFactory<C> connections;
protected final TimeValue timeOut;
protected final ScheduledExecutorService executor;
protected ServerViewFactories(
ClientConnectionFactory<C> connections,
TimeValue timeOut,
ScheduledExecutorService executor) {
this.connections = connections;
this.timeOut = timeOut;
this.executor = executor;
}
@Override
public ServerViewFactory<Session, OperationClientExecutor<C>> get(ServerInetAddressView view) {
return ServerViewFactory.defaults(connections, view, timeOut, executor);
}
}
protected final Function<ServerInetAddressView[], ServerInetAddressView> selector;
protected final EnsembleView<ServerInetAddressView> view;
protected final ParameterizedFactory<ServerInetAddressView,T> factory;
protected EnsembleViewFactory(
EnsembleView<ServerInetAddressView> view,
Function<ServerInetAddressView[], ServerInetAddressView> selector,
ParameterizedFactory<ServerInetAddressView,T> factory) {
this.view = view;
this.selector = selector;
this.factory = factory;
}
public EnsembleView<ServerInetAddressView> view() {
return view;
}
public ServerInetAddressView select() {
return selector.apply(Iterables.toArray(view, ServerInetAddressView.class));
}
@Override
public T get() {
return get(select());
}
@Override
public T get(ServerInetAddressView server) {
if (! view().contains(server)) {
throw new IllegalArgumentException(server.toString());
}
return factory.get(server);
}
}