All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nesscomputing.service.discovery.client.internal.ServiceDiscoveryAnnouncer Maven / Gradle / Ivy

There is a newer version: 1.6.3
Show newest version
/**
 * 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