package edu.uw.zookeeper.client; import static com.google.common.base.Preconditions.checkState; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.util.concurrent.AbstractIdleService; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Service; import edu.uw.zookeeper.EnsembleView; import edu.uw.zookeeper.ServerInetAddressView; import edu.uw.zookeeper.ZooKeeperApplication; import edu.uw.zookeeper.client.EnsembleViewConfiguration; import edu.uw.zookeeper.client.EnsembleViewFactory; import edu.uw.zookeeper.client.ServerViewFactory; import edu.uw.zookeeper.common.Automaton; import edu.uw.zookeeper.common.DefaultsFactory; import edu.uw.zookeeper.common.Pair; import edu.uw.zookeeper.common.Reference; import edu.uw.zookeeper.common.RuntimeModule; import edu.uw.zookeeper.data.Operations; import edu.uw.zookeeper.net.ClientConnectionFactory; import edu.uw.zookeeper.net.Connection; import edu.uw.zookeeper.protocol.Message; import edu.uw.zookeeper.protocol.Operation; import edu.uw.zookeeper.protocol.ProtocolConnection; import edu.uw.zookeeper.protocol.ProtocolRequestMessage; import edu.uw.zookeeper.protocol.ProtocolState; import edu.uw.zookeeper.protocol.Session; import edu.uw.zookeeper.protocol.client.ClientConnectionFactoryBuilder; import edu.uw.zookeeper.protocol.client.ConnectionClientExecutor; import edu.uw.zookeeper.protocol.client.OperationClientExecutor; public class ConnectionClientExecutorsService <I extends Operation.Request, T, C extends ConnectionClientExecutor<I,?,?,?>> extends AbstractIdleService implements DefaultsFactory<T, ListenableFuture<C>>, Function<C, C>, Iterable<C> { public static <I extends Operation.Request, T, C extends ConnectionClientExecutor<I,?,?,?>> ConnectionClientExecutorsService<I,T,C> newInstance( DefaultsFactory<T, ? extends ListenableFuture<? extends C>> factory) { return new ConnectionClientExecutorsService<I,T,C>(factory); } public static OperationBuilder builder() { return new OperationBuilder(null, null, null, null); } public static abstract class AbstractBuilder<T extends ConnectionClientExecutorsService<?,?,?>, C extends AbstractBuilder<T,C>> implements ZooKeeperApplication.RuntimeBuilder<List<Service>, C> { protected final RuntimeModule runtime; protected final ClientConnectionFactoryBuilder connectionBuilder; protected final ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> clientConnectionFactory; protected final T clientExecutors; protected AbstractBuilder( ClientConnectionFactoryBuilder connectionBuilder, ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> clientConnectionFactory, T clientExecutors, RuntimeModule runtime) { this.connectionBuilder = connectionBuilder; this.clientConnectionFactory = clientConnectionFactory; this.clientExecutors = clientExecutors; this.runtime = runtime; } @Override public RuntimeModule getRuntimeModule() { return runtime; } @SuppressWarnings("unchecked") @Override public C setRuntimeModule(RuntimeModule runtime) { if (this.runtime == runtime) { return (C) this; } else { return newInstance( (connectionBuilder == null) ? connectionBuilder : connectionBuilder.setRuntimeModule(runtime), clientConnectionFactory, clientExecutors, runtime); } } public ClientConnectionFactoryBuilder getConnectionBuilder() { return connectionBuilder; } @SuppressWarnings("unchecked") public C setConnectionBuilder(ClientConnectionFactoryBuilder connectionBuilder) { if (this.connectionBuilder == connectionBuilder) { return (C) this; } else { return newInstance(connectionBuilder.setRuntimeModule(getRuntimeModule()), clientConnectionFactory, clientExecutors, runtime); } } public ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> getClientConnectionFactory() { return clientConnectionFactory; } @SuppressWarnings("unchecked") public C setClientConnectionFactory( ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> clientConnectionFactory) { if (this.clientConnectionFactory == clientConnectionFactory) { return (C) this; } else { return newInstance(connectionBuilder, clientConnectionFactory, clientExecutors, runtime); } } public T getConnectionClientExecutors() { return clientExecutors; } @SuppressWarnings("unchecked") public C setConnectionClientExecutors( T clientExecutors) { if (this.clientExecutors == clientExecutors) { return (C) this; } else { return newInstance(connectionBuilder, clientConnectionFactory, clientExecutors, runtime); } } @SuppressWarnings("unchecked") @Override public C setDefaults() { checkState(getRuntimeModule() != null); if (this.connectionBuilder == null) { return setConnectionBuilder(getDefaultClientConnectionFactoryBuilder()).setDefaults(); } ClientConnectionFactoryBuilder connectionBuilder = this.connectionBuilder.setDefaults(); if (this.connectionBuilder != connectionBuilder) { return setConnectionBuilder(connectionBuilder).setDefaults(); } if (clientConnectionFactory == null) { return setClientConnectionFactory(getDefaultClientConnectionFactory()).setDefaults(); } if (clientExecutors == null) { return setConnectionClientExecutors(getDefaultConnectionClientExecutorsService()).setDefaults(); } return (C) this; } @Override public List<Service> build() { return setDefaults().doBuild(); } protected List<Service> doBuild() { return Lists.<Service>newArrayList( clientConnectionFactory, clientExecutors); } protected abstract C newInstance( ClientConnectionFactoryBuilder connectionBuilder, ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> clientConnectionFactory, T clientExecutors, RuntimeModule runtime); protected ClientConnectionFactoryBuilder getDefaultClientConnectionFactoryBuilder() { return ClientConnectionFactoryBuilder.defaults() .setRuntimeModule(runtime).setDefaults(); } protected ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> getDefaultClientConnectionFactory() { return connectionBuilder.build(); } protected abstract T getDefaultConnectionClientExecutorsService(); } public static class OperationBuilder extends AbstractBuilder<ConnectionClientExecutorsService<Operation.Request, Session, OperationClientExecutor<?>>, OperationBuilder> { protected OperationBuilder( ClientConnectionFactoryBuilder connectionBuilder, ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> clientConnectionFactory, ConnectionClientExecutorsService<Operation.Request, Session, OperationClientExecutor<?>> clientExecutors, RuntimeModule runtime) { super(connectionBuilder, clientConnectionFactory, clientExecutors, runtime); } @Override protected OperationBuilder newInstance( ClientConnectionFactoryBuilder connectionBuilder, ClientConnectionFactory<? extends ProtocolConnection<Message.ClientSession, Message.ServerSession,?,?,?>> clientConnectionFactory, ConnectionClientExecutorsService<Operation.Request, Session, OperationClientExecutor<?>> clientExecutors, RuntimeModule runtime) { return new OperationBuilder(connectionBuilder, clientConnectionFactory, clientExecutors, runtime); } @Override protected ConnectionClientExecutorsService<Operation.Request, Session, OperationClientExecutor<?>> getDefaultConnectionClientExecutorsService() { EnsembleView<ServerInetAddressView> ensemble = EnsembleViewConfiguration.get(getRuntimeModule().getConfiguration()); final EnsembleViewFactory<? extends ServerViewFactory<Session, ? extends OperationClientExecutor<?>>> ensembleFactory = EnsembleViewFactory.fromSession( getClientConnectionFactory(), ensemble, getConnectionBuilder().getTimeOut(), getRuntimeModule().getExecutors().get(ScheduledExecutorService.class)); ConnectionClientExecutorsService<Operation.Request, Session, OperationClientExecutor<?>> service = ConnectionClientExecutorsService.newInstance( new DefaultsFactory<Session, ListenableFuture<? extends OperationClientExecutor<?>>>() { @Override public ListenableFuture<? extends OperationClientExecutor<?>> get(Session value) { return ensembleFactory.get().get(value); } @Override public ListenableFuture<? extends OperationClientExecutor<?>> get() { return ensembleFactory.get().get(); } }); return service; } } protected final Logger logger = LogManager.getLogger(getClass()); protected final Executor executor; protected final DefaultsFactory<T, ? extends ListenableFuture<? extends C>> factory; protected final Set<C> executors; protected ConnectionClientExecutorsService( DefaultsFactory<T, ? extends ListenableFuture<? extends C>> factory) { this.executor = MoreExecutors.directExecutor(); this.factory = factory; this.executors = Collections.synchronizedSet(Sets.<C>newHashSet()); } @Override public ListenableFuture<C> get(T value) { checkState(isRunning()); return Futures.transform(factory.get(value), this, executor); } @Override public ListenableFuture<C> get() { checkState(isRunning()); return Futures.transform(factory.get(), this, executor); } @Override public C apply(C input) { new ClientHandler(input); return input; } @Override public Iterator<C> iterator() { ImmutableSet.Builder<C> copy = ImmutableSet.builder(); synchronized (executors) { copy.addAll(executors); } return copy.build().iterator(); } @Override protected void startUp() throws Exception { } @Override protected void shutDown() throws Exception { @SuppressWarnings("unchecked") I disconnect = (I) ProtocolRequestMessage.of(0, Operations.Requests.disconnect().build()); List<Pair<C, ListenableFuture<? extends Operation.ProtocolResponse<?>>>> futures = Lists.newArrayListWithExpectedSize(executors.size()); for (C c: this) { ListenableFuture<? extends Operation.ProtocolResponse<?>> future = null; try { if ((c.connection().codec().state() == ProtocolState.CONNECTED) && (c.connection().state().compareTo(Connection.State.CONNECTION_CLOSING) < 0)) { future = c.submit(disconnect); } else { future = null; } } catch (Exception e) { future = Futures.immediateFailedFuture(e); } futures.add(Pair.<C, ListenableFuture<? extends Operation.ProtocolResponse<?>>>create(c, future)); } for (Pair<C, ListenableFuture<? extends Operation.ProtocolResponse<?>>> future: futures) { try { if (future.second() != null) { int timeOut = future.first().session().get().getTimeOut(); if (timeOut > 0) { future.second().get(timeOut, TimeUnit.MILLISECONDS); } else { future.second().get(); } } } catch (Exception e) { logger.debug("", e); } finally { future.first().connection().close(); } } } protected class ClientHandler implements Reference<C>, Connection.Listener<Object> { protected final C instance; public ClientHandler(C instance) { this.instance = instance; executors.add(instance); instance.connection().subscribe(this); if (! isRunning()) { instance.connection().close(); throw new IllegalStateException(String.valueOf(state())); } if (instance.connection().state() == Connection.State.CONNECTION_CLOSED) { handleConnectionState(Automaton.Transition.create(instance.connection().state(), instance.connection().state())); } } @Override public C get() { return instance; } @Override public void handleConnectionState( Automaton.Transition<Connection.State> state) { if (state.to() == Connection.State.CONNECTION_CLOSED) { instance.connection().unsubscribe(this); executors.remove(instance); } } @Override public void handleConnectionRead(Object message) { } } }