private OperationFuture<String> createLiveNode() { String liveNode = getLiveNodePath(); LOG.info("Create live node {}{}", zkClient.getConnectString(), liveNode); JsonObject content = new JsonObject(); content.add("data", liveNodeData.get()); return ZKOperations.ignoreError( zkClient.create(liveNode, encodeJson(content), CreateMode.EPHEMERAL), KeeperException.NodeExistsException.class, liveNode); }
private void watchStateNode() { if (!shouldProcessZKEvent()) { return; } Futures.addCallback( zkClient.getData( getZKPath("state"), new Watcher() { @Override public void process(WatchedEvent event) { if (!shouldProcessZKEvent()) { return; } switch (event.getType()) { case NodeDataChanged: watchStateNode(); break; default: LOG.debug("Ignore ZK event for state node: {}", event); } } }), stateNodeDataCallback, Threads.SAME_THREAD_EXECUTOR); }
protected final void watchInstanceNode() { if (!shouldProcessZKEvent()) { return; } Futures.addCallback( zkClient.getData( getInstancePath(), new Watcher() { @Override public void process(WatchedEvent event) { if (!shouldProcessZKEvent()) { return; } switch (event.getType()) { case NodeDataChanged: watchInstanceNode(); break; case NodeDeleted: instanceNodeFailed( KeeperException.create(KeeperException.Code.NONODE, getInstancePath())); break; default: LOG.info("Ignore ZK event for instance node: {}", event); } } }), instanceNodeDataCallback, Threads.SAME_THREAD_EXECUTOR); }
public void onReceived( Executor executor, final String path, final int version, final String id, final Message message) { if (callback == null) { // Simply delete the message if (LOG.isDebugEnabled()) { LOG.debug("Ignoring incoming message from " + path + ": " + message); } listenFailure(zkClient.delete(path, version)); return; } executor.execute( new Runnable() { @Override public void run() { try { // Message process is synchronous for now. Making it async needs more thoughts about // race conditions. // The executor is the callbackExecutor which is a single thread executor. callback.onReceived(id, message).get(); } catch (Throwable t) { LOG.error("Exception when processing message: {}, {}, {}", id, message, path, t); } finally { listenFailure(zkClient.delete(path, version)); } } }); }
private void processMessage(final String path, final String messageId) { Futures.addCallback( zkClient.getData(path), new FutureCallback<NodeData>() { @Override public void onSuccess(NodeData result) { Message message = MessageCodec.decode(result.getData()); if (message == null) { LOG.error("Failed to decode message for " + messageId + " in " + path); listenFailure(zkClient.delete(path, result.getStat().getVersion())); return; } if (LOG.isDebugEnabled()) { LOG.debug( "Message received from " + path + ": " + new String(MessageCodec.encode(message), Charsets.UTF_8)); } if (handleStopMessage( message, getDeleteSupplier(path, result.getStat().getVersion()))) { return; } messageCallback.onReceived( callbackExecutor, path, result.getStat().getVersion(), messageId, message); } @Override public void onFailure(Throwable t) { LOG.error("Failed to fetch message content.", t); } }); }
private void watchMessages() { final String messagesPath = getZKPath("messages"); Futures.addCallback( zkClient.getChildren( messagesPath, new Watcher() { @Override public void process(WatchedEvent event) { // TODO: Do we need to deal with other type of events? if (event.getType() == Event.EventType.NodeChildrenChanged && decoratedService.isRunning()) { watchMessages(); } } }), new FutureCallback<NodeChildren>() { @Override public void onSuccess(NodeChildren result) { // Sort by the name, which is the messageId. Assumption is that message ids is ordered // by time. List<String> messages = Lists.newArrayList(result.getChildren()); Collections.sort(messages); for (String messageId : messages) { processMessage(messagesPath + "/" + messageId, messageId); } } @Override public void onFailure(Throwable t) { // TODO: what could be done besides just logging? LOG.error("Failed to watch messages.", t); } }); }
private void actOnExists(final String path, final Runnable action) { // Watch for node existence. final AtomicBoolean nodeExists = new AtomicBoolean(false); Futures.addCallback( zkClient.exists( path, new Watcher() { @Override public void process(WatchedEvent event) { if (!shouldProcessZKEvent()) { return; } // When node is created, call the action. // Other event type would be handled by the action. if (event.getType() == Event.EventType.NodeCreated && nodeExists.compareAndSet(false, true)) { action.run(); } } }), new FutureCallback<Stat>() { @Override public void onSuccess(Stat result) { if (result != null && nodeExists.compareAndSet(false, true)) { action.run(); } } @Override public void onFailure(Throwable t) { LOG.error("Failed in exists call to {}. Shutting down service.", path, t); forceShutDown(); } }, Threads.SAME_THREAD_EXECUTOR); }
private OperationFuture<String> removeServiceNode() { String serviceNode = String.format("/%s", id.getId()); LOG.info("Remove service node {}{}", zkClient.getConnectString(), serviceNode); return ZKOperations.recursiveDelete(zkClient, serviceNode); }
private OperationFuture<String> removeLiveNode() { String liveNode = getLiveNodePath(); LOG.info("Remove live node {}{}", zkClient.getConnectString(), liveNode); return ZKOperations.ignoreError( zkClient.delete(liveNode), KeeperException.NoNodeException.class, liveNode); }
@Override protected void doStart() { callbackExecutor = Executors.newSingleThreadExecutor(Threads.createDaemonThreadFactory("message-callback")); // Create the live node, if succeeded, start the decorated service, otherwise fail out. Futures.addCallback( createLiveNode(), new FutureCallback<String>() { @Override public void onSuccess(String result) { // Create nodes for states and messaging StateNode stateNode = new StateNode(ServiceController.State.STARTING); final ListenableFuture<List<String>> createFuture = Futures.allAsList( ZKOperations.ignoreError( zkClient.create(getZKPath("messages"), null, CreateMode.PERSISTENT), KeeperException.NodeExistsException.class, null), zkClient.create( getZKPath("state"), encodeStateNode(stateNode), CreateMode.PERSISTENT)); createFuture.addListener( new Runnable() { @Override public void run() { try { createFuture.get(); // Starts the decorated service decoratedService.addListener(createListener(), Threads.SAME_THREAD_EXECUTOR); decoratedService.start(); } catch (Exception e) { notifyFailed(e); } } }, Threads.SAME_THREAD_EXECUTOR); } @Override public void onFailure(Throwable t) { notifyFailed(t); } }); // Watch for session expiration, recreate the live node if reconnected after expiration. zkClient.addConnectionWatcher( new Watcher() { private boolean expired = false; @Override public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.Expired) { LOG.warn( "ZK Session expired for service {} with runId {}.", decoratedService, id.getId()); expired = true; } else if (event.getState() == Event.KeeperState.SyncConnected && expired) { LOG.info( "Reconnected after expiration for service {} with runId {}", decoratedService, id.getId()); expired = false; Futures.addCallback( createLiveNode(), new FutureCallback<String>() { @Override public void onSuccess(String result) { // All good, no-op } @Override public void onFailure(Throwable t) { notifyFailed(t); } }, Threads.SAME_THREAD_EXECUTOR); } } }); }