package edu.uw.zookeeper.server; import static com.google.common.base.Preconditions.checkState; import java.util.Map; import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.zookeeper.KeeperException; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Queues; import com.google.common.util.concurrent.ListenableFuture; import edu.uw.zookeeper.ZooKeeperApplication; import edu.uw.zookeeper.common.Actors.ExecutedQueuedActor; import edu.uw.zookeeper.common.Processor; import edu.uw.zookeeper.common.Processors; import edu.uw.zookeeper.common.PromiseTask; import edu.uw.zookeeper.common.RuntimeModule; import edu.uw.zookeeper.common.TaskExecutor; import edu.uw.zookeeper.data.NameTrie; import edu.uw.zookeeper.data.ZNodeNode; import edu.uw.zookeeper.data.SimpleLabelTrie; import edu.uw.zookeeper.data.TxnOperation; import edu.uw.zookeeper.protocol.Message; import edu.uw.zookeeper.protocol.NotificationListener; import edu.uw.zookeeper.protocol.Operation; import edu.uw.zookeeper.protocol.SessionOperation; import edu.uw.zookeeper.protocol.proto.IDisconnectResponse; import edu.uw.zookeeper.protocol.proto.IPingResponse; import edu.uw.zookeeper.protocol.proto.IWatcherEvent; import edu.uw.zookeeper.protocol.proto.OpCode; import edu.uw.zookeeper.protocol.proto.Records; import edu.uw.zookeeper.protocol.server.AssignZxidProcessor; import edu.uw.zookeeper.protocol.server.ZxidEpochIncrementer; import edu.uw.zookeeper.protocol.server.ZxidGenerator; public class SimpleServer extends ExecutedQueuedActor<PromiseTask<SessionOperation.Request<?>, Message.ServerResponse<?>>> implements TaskExecutor<SessionOperation.Request<?>, Message.ServerResponse<?>> { public static abstract class Builder<C extends Builder<C>> implements ZooKeeperApplication.RuntimeBuilder<SimpleServer, C> { protected final RuntimeModule runtime; protected final ZxidGenerator zxids; protected final NameTrie<ZNodeNode> data; protected final SessionManager sessions; protected final ReentrantReadWriteLock lock; protected final Watches dataWatches; protected final Watches childWatches; protected final Function<Long, ? extends NotificationListener<Operation.ProtocolResponse<IWatcherEvent>>> listeners; protected Builder( ZxidGenerator zxids, NameTrie<ZNodeNode> data, SessionManager sessions, ReentrantReadWriteLock lock, Watches dataWatches, Watches childWatches, Function<Long, ? extends NotificationListener<Operation.ProtocolResponse<IWatcherEvent>>> listeners, RuntimeModule runtime) { this.zxids = zxids; this.data = data; this.sessions = sessions; this.dataWatches = dataWatches; this.childWatches = childWatches; this.runtime = runtime; this.listeners = listeners; this.lock = lock; } @Override public RuntimeModule getRuntimeModule() { return runtime; } @Override public C setRuntimeModule(RuntimeModule runtime) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public ReentrantReadWriteLock getLock() { return lock; } public C setLock(ReentrantReadWriteLock lock) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public Watches getDataWatches() { return dataWatches; } public C setDataWatches(Watches dataWatches) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public Watches getChildWatches() { return childWatches; } public C setChildWatches(Watches childWatches) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public ZxidGenerator getZxids() { return zxids; } public C setZxids(ZxidGenerator zxids) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public ZxidGenerator getDefaultZxids() { return ZxidEpochIncrementer.fromZero(); } public NameTrie<ZNodeNode> getData() { return data; } public C setData(NameTrie<ZNodeNode> data) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public NameTrie<ZNodeNode> getDefaultData() { return SimpleLabelTrie.forRoot(ZNodeNode.root()); } public SessionManager getSessions() { return sessions; } public C setSessions(SessionManager sessions) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } public Function<Long, ? extends NotificationListener<Operation.ProtocolResponse<IWatcherEvent>>> getListeners() { return listeners; } public C setListeners(Function<Long, ? extends NotificationListener<Operation.ProtocolResponse<IWatcherEvent>>> listeners) { return newInstance(zxids, data, sessions, lock, dataWatches, childWatches, listeners, runtime); } @SuppressWarnings("unchecked") @Override public C setDefaults() { checkState(getRuntimeModule() != null); if (getLock() == null) { return setLock(getDefaultLock()).setDefaults(); } if (getZxids() == null) { return setZxids(getDefaultZxids()).setDefaults(); } if (getData() == null) { return setData(getDefaultData()).setDefaults(); } if (getSessions() == null) { return setSessions(getDefaultSessions()).setDefaults(); } if (getListeners() == null) { return setListeners(getDefaultListeners()).setDefaults(); } if (getDataWatches() == null) { return setDataWatches(getDefaultWatches()).setDefaults(); } if (getChildWatches() == null) { return setChildWatches(getDefaultWatches()).setDefaults(); } return (C) this; } @Override public SimpleServer build() { return setDefaults().doBuild(); } protected abstract C newInstance( ZxidGenerator zxids, NameTrie<ZNodeNode> data, SessionManager sessions, ReentrantReadWriteLock lock, Watches dataWatches, Watches childWatches, Function<Long, ? extends NotificationListener<Operation.ProtocolResponse<IWatcherEvent>>> listeners, RuntimeModule runtime); protected SimpleServer doBuild() { return SimpleServer.newInstance( getLock(), getDefaultProcessor(), getRuntimeModule().getExecutors().get(ExecutorService.class)); } protected abstract Function<Long, ? extends NotificationListener<Operation.ProtocolResponse<IWatcherEvent>>> getDefaultListeners(); protected abstract SessionManager getDefaultSessions(); protected ReentrantReadWriteLock getDefaultLock() { return new ReentrantReadWriteLock(); } protected Watches getDefaultWatches() { return Watches.create(getListeners()); } protected Processor<SessionOperation.Request<?>, Message.ServerResponse<?>> getDefaultProcessor() { Processor<SessionOperation.Request<?>, Message.ServerResponse<?>> processor = Processors.bridge( ToTxnRequestProcessor.create( AssignZxidProcessor.newInstance(getZxids())), ProtocolResponseProcessor.create( getDefaultTxnProcessor())); return processor; } protected Processors.UncheckedProcessor<TxnOperation.Request<?>, Records.Response> getDefaultTxnProcessor() { Map<OpCode, Processors.CheckedProcessor<TxnOperation.Request<?>, ? extends Records.Response, KeeperException>> processors = Maps.newEnumMap(OpCode.class); processors = ZNodeNode.Operators.of(getData(), processors); processors.put(OpCode.MULTI, ZNodeNode.MultiOperator.of( getData(), ByOpcodeTxnRequestProcessor.create(ImmutableMap.copyOf(processors)))); processors.put(OpCode.CLOSE_SESSION, new Processors.CheckedProcessor<TxnOperation.Request<?>, IDisconnectResponse, KeeperException>() { @Override public IDisconnectResponse apply( TxnOperation.Request<?> request) throws KeeperException { if (getSessions().remove(request.getSessionId()) == null) { throw new KeeperException.SessionMovedException(); } return Records.newInstance(IDisconnectResponse.class); } }); processors.put(OpCode.PING, new Processors.CheckedProcessor<TxnOperation.Request<?>, IPingResponse, KeeperException>() { @Override public IPingResponse apply( TxnOperation.Request<?> request) throws KeeperException { return Records.newInstance(IPingResponse.class); } }); return EphemeralProcessor.create( RequestErrorProcessor.<TxnOperation.Request<?>>create( WatcherEventProcessor.create( ByOpcodeTxnRequestProcessor.create( ImmutableMap.copyOf(processors)), getDataWatches(), getChildWatches()))); } } public static SimpleServer newInstance( ReentrantReadWriteLock lock, Processor<? super SessionOperation.Request<?>, ? extends Message.ServerResponse<?>> processor, Executor executor) { return new SimpleServer( lock, processor, executor, Queues.<PromiseTask<SessionOperation.Request<?>, Message.ServerResponse<?>>>newConcurrentLinkedQueue(), LogManager.getLogger(SimpleServer.class)); } protected final ReentrantReadWriteLock lock; protected final Processor<? super SessionOperation.Request<?>, ? extends Message.ServerResponse<?>> processor; protected SimpleServer( ReentrantReadWriteLock lock, Processor<? super SessionOperation.Request<?>, ? extends Message.ServerResponse<?>> processor, Executor executor, Queue<PromiseTask<SessionOperation.Request<?>, Message.ServerResponse<?>>> mailbox, Logger logger) { super(executor, mailbox, logger); this.lock = lock; this.processor = processor; } @Override public ListenableFuture<Message.ServerResponse<?>> submit(SessionOperation.Request<?> request) { PromiseTask<SessionOperation.Request<?>, Message.ServerResponse<?>> task = PromiseTask.<SessionOperation.Request<?>, Message.ServerResponse<?>>of(request); if (! send(task)) { task.cancel(true); } return task; } @Override protected boolean apply(PromiseTask<SessionOperation.Request<?>, Message.ServerResponse<?>> input) { lock.writeLock().lock(); try { if (! input.isDone()) { try { Message.ServerResponse<?> response = processor.apply(input.task()); input.set(response); } catch (Exception e) { input.setException(e); } } } finally { lock.writeLock().unlock(); } return true; } @Override protected void doStop() { Future<?> task; while ((task = mailbox.poll()) != null) { task.cancel(true); } } }