package org.robotninjas.barge.jaxrs.ws; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Sets; import org.jetlang.core.RunnableExecutorImpl; import org.jetlang.fibers.Fiber; import org.jetlang.fibers.ThreadFiber; import org.robotninjas.barge.api.AppendEntries; import org.robotninjas.barge.api.RequestVote; import org.robotninjas.barge.state.Raft; import org.robotninjas.barge.state.RaftProtocolListener; import org.robotninjas.barge.state.StateTransitionListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Set; /** */ public class WsEventListener implements StateTransitionListener, RaftProtocolListener { private final Logger logger = LoggerFactory.getLogger(WsEventListener.class); private final Fiber executor; private final ObjectMapper json = new ObjectMapper(); private Set<Listener> remotes = Sets.newConcurrentHashSet(); public WsEventListener() { this(new ThreadFiber(new RunnableExecutorImpl(), "ws-events", true)); } public WsEventListener(Fiber fiber) { this.executor = fiber; } public void start() { executor.start(); } public void stop() { executor.dispose(); } @Override public void changeState(@Nonnull final Raft context, @Nullable final Raft.StateType from, @Nonnull final Raft.StateType to) { dispatch(WsMessages.stateChange(context, from, to)); } @Override public void invalidTransition(@Nonnull final Raft context, @Nonnull final Raft.StateType actual, @Nullable final Raft.StateType expected) { dispatch(WsMessages.invalidTransition(context, expected, actual)); } @Override public void stop(@Nonnull final Raft raft) { dispatch(WsMessages.stopping(raft)); } @Override public void init(final Raft raft) { dispatch(WsMessages.init(raft)); } @Override public void appendEntries(@Nonnull final Raft raft, @Nonnull final AppendEntries entries) { dispatch(WsMessages.appendEntries(raft, entries)); } @Override public void requestVote(@Nonnull final Raft raft, @Nonnull final RequestVote vote) { dispatch(WsMessages.requestVote(raft, vote)); } @Override public void commit(@Nonnull final Raft raft, @Nonnull final byte[] operation) { dispatch(WsMessages.commit(raft, operation)); } public void addClient(Listener listener) { logger.info("adding listener: {}", listener); remotes.add(listener); } public void removeClient(Listener listener) { logger.info("removing listener: {}", listener); remotes.remove(listener); } public void error(EventSocket socket, Throwable error) { logger.warn("caught error on socket {}", socket, error); } private void send(WsMessage message, Listener remote) { try { remote.send(json.writeValueAsString(message)); } catch (JsonProcessingException e) { logger.warn("fail to convert {} to json", message, e); } } private void dispatch(final WsMessage message) { executor.execute(new Runnable() { @Override public void run() { for (Listener remote : remotes) { send(message, remote); } } }); } }