com.nesscomputing.service.discovery.client.internal.ServiceDiscoveryAnnouncer Maven / Gradle / Ivy
/**
* Copyright (C) 2012 Ness Computing, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nesscomputing.service.discovery.client.internal;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicLong;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import com.nesscomputing.logging.Log;
import com.nesscomputing.service.discovery.client.DiscoveryClientConfig;
import com.nesscomputing.service.discovery.client.ServiceInformation;
/**
* Maintains the local announcements on the central service discovery directory and ensures that they are present even
* if the client disconnects / reconnects.
*/
class ServiceDiscoveryAnnouncer extends ServiceDiscoveryTask
{
private static final Log LOG = Log.findLog();
/** Set of all the announcements managed by this client. */
private final Set localAnnouncements = new CopyOnWriteArraySet();
/** Map of the local announcements to the last generation in which it was present. */
private final Map localAnnouncementGenerations = Maps.newHashMap();
private final Set staticAnnouncementsToRemove = new CopyOnWriteArraySet();
// Generation counter for the announcements. By setting the start value to 1, this
// ensures an immediate run through the announcer.
private volatile long lastAnnouncementGeneration = 0L;
private final AtomicLong announcementGeneration = new AtomicLong(1L);
ServiceDiscoveryAnnouncer(final DiscoveryClientConfig discoveryConfig, final ObjectMapper objectMapper)
{
super(discoveryConfig, objectMapper);
}
void unannounceAll()
{
localAnnouncements.clear();
announcementGeneration.incrementAndGet();
}
void announce(final ServiceInformation serviceInformation)
{
localAnnouncements.add(serviceInformation);
announcementGeneration.incrementAndGet();
}
void unannounce(final ServiceInformation serviceInformation)
{
localAnnouncements.remove(serviceInformation);
announcementGeneration.incrementAndGet();
if (serviceInformation.isStaticAnnouncement()) {
staticAnnouncementsToRemove.add(serviceInformation);
}
}
@Override
void determineGeneration(final AtomicLong generation, final long tick)
{
final long currentAnnouncementGeneration = announcementGeneration.get();
// Trigger a run through the work loop if the last announcement was before
// the current generation.
if (lastAnnouncementGeneration < currentAnnouncementGeneration) {
generation.incrementAndGet();
lastAnnouncementGeneration = currentAnnouncementGeneration;
}
}
@Override
void visit(final List childNodes, final ZooKeeper zookeeper, final long currentGeneration) throws InterruptedException, KeeperException
{
// Loop through everything that we *should* announce, add them with the current generation to the
// generation map.
for (final ServiceInformation si : localAnnouncements) {
// This announcement is safe, so increment its generation.
localAnnouncementGenerations.put(si.getAnnouncementName(), currentGeneration);
// If announcement is not present, announce it.
if (!childNodes.contains(si.getAnnouncementName())) {
LOG.debug("Need to announce %s", si.getAnnouncementName());
final String childPath = getNodePath(si.getAnnouncementName());
try {
final byte [] serialized = objectMapper.writeValueAsBytes(si);
zookeeper.create(childPath, serialized, Ids.OPEN_ACL_UNSAFE, si.isStaticAnnouncement() ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL);
LOG.debug("Created %sannouncement for %s", si.isStaticAnnouncement() ? "static " : "", si.getAnnouncementName());
}
catch (final IOException ioe) {
LOG.warn(ioe, "While generating announcement:");
}
}
}
// Now loop through everything that is currently present in the generation map. Remove everything that
// is not marked with the current generation.
for (final Iterator> it = localAnnouncementGenerations.entrySet().iterator(); it.hasNext(); ) {
final Map.Entry entry = it.next();
if (entry.getValue() == currentGeneration) {
LOG.trace("Announcement %s survives in generation %d", entry.getKey(), entry.getValue());
continue;
}
else {
LOG.trace("Announcement %s no longer present in generation %d", entry.getKey(), entry.getValue());
final String childPath = getNodePath(entry.getKey());
try {
zookeeper.delete(childPath, -1);
LOG.debug("Removed announcement for %s", entry.getKey());
it.remove();
}
catch (final KeeperException ke) {
// The node disappeared under us. That should not happen, but
// test for it anyway.
if (ke.code() == Code.NONODE) {
LOG.trace("Node was already removed, ignoring");
it.remove();
}
else {
throw ke;
}
}
}
}
// Now remove static announcements which have been unannounced
for (final ServiceInformation si : staticAnnouncementsToRemove)
{
final String announcementName = si.getAnnouncementName();
LOG.info("Removing static announcement %s", announcementName);
final String childPath = getNodePath(announcementName);
try {
zookeeper.delete(childPath, -1);
LOG.debug("Removed announcement for %s", announcementName);
staticAnnouncementsToRemove.remove(si);
}
catch (final KeeperException ke) {
// The node disappeared under us. That should not happen, but
// test for it anyway.
if (ke.code() == Code.NONODE) {
LOG.trace("Node was already removed, ignoring");
staticAnnouncementsToRemove.remove(si);
}
else {
throw ke;
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy