<commit_before>package com.bazaarvoice.soa.registry;

import com.bazaarvoice.soa.ServiceEndPoint;
import com.bazaarvoice.soa.ServiceEndPointJsonCodec;
import com.bazaarvoice.soa.ServiceRegistry;
import com.bazaarvoice.soa.metrics.UniqueMetricSource;
import com.bazaarvoice.zookeeper.ZooKeeperConnection;
import com.bazaarvoice.zookeeper.recipes.ZooKeeperPersistentEphemeralNode;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.netflix.curator.utils.ZKPaths;
import com.yammer.metrics.core.Gauge;
import org.apache.zookeeper.CreateMode;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * A <code>ServiceRegistry</code> implementation that uses ZooKeeper as its backing data store.
 */
public class ZooKeeperServiceRegistry implements ServiceRegistry
{
    /** The root path in ZooKeeper for where service registrations are stored. */
    @VisibleForTesting
    static final String ROOT_SERVICES_PATH = "ostrich";

    /** Maximum number of bytes that can be stored on a node in ZooKeeper. */
    @VisibleForTesting
    static final int MAX_DATA_SIZE = 1024 * 1024;

    // All dates are represented in ISO-8601 format and in the UTC time zone.
    @VisibleForTesting
    static final DateTimeFormatter ISO8601 = ISODateTimeFormat.dateTime().withZoneUTC();

    private final ZooKeeperConnection _zooKeeperConnection;
    private volatile boolean _closed = false;

    /** The ephemeral data that's been written to ZooKeeper.  Saved in case the connection is lost and then regained. */
    private final Map<String, ZooKeeperPersistentEphemeralNode> _nodes = Maps.newConcurrentMap();

    private final UniqueMetricSource _metricSource = new UniqueMetricSource(ZooKeeperServiceRegistry.class);

    public ZooKeeperServiceRegistry(ZooKeeperConnection connection) {
        checkNotNull(connection);
        _zooKeeperConnection = connection;
        _metricSource.newGauge("registered-end-points", new Gauge<Integer>() {
            @Override
            public Integer value() {
                return _nodes.size();
            }
        });
    }

    /** {@inheritDoc} */
    @Override
    public void register(ServiceEndPoint endPoint) {
        register(endPoint, true);
    }

    @VisibleForTesting
    void register(ServiceEndPoint endPoint, boolean includeRegistrationTime) {
        checkState(!_closed);
        checkNotNull(endPoint);

        Map<String, Object> registrationData = Maps.newHashMap();
        if (includeRegistrationTime) {
            DateTime now = DateTime.now().toDateTime(DateTimeZone.UTC);
            registrationData.put("registration-time", ISO8601.print(now));
        }

        byte[] data = ServiceEndPointJsonCodec.toJson(endPoint, registrationData).getBytes(Charsets.UTF_8);
        checkState(data.length < MAX_DATA_SIZE, "Serialized form of ServiceEndPoint must be < 1MB.");

        String path = makeEndPointPath(endPoint);
        _nodes.put(path, new ZooKeeperPersistentEphemeralNode(_zooKeeperConnection, path, data, CreateMode.EPHEMERAL));
    }

    /** {@inheritDoc} */
    @Override
    public void unregister(ServiceEndPoint endPoint) {
        checkState(!_closed);
        checkNotNull(endPoint);

        String path = makeEndPointPath(endPoint);
        ZooKeeperPersistentEphemeralNode node = _nodes.remove(path);
        if (node != null) {
            node.close(10, TimeUnit.SECONDS);
        }
    }

    @Override
    public synchronized void close() throws IOException {
        if (_closed) {
            return;
        }

        _closed = true;

        for (ZooKeeperPersistentEphemeralNode node : _nodes.values()) {
            node.close(10, TimeUnit.SECONDS);
        }
        _nodes.clear();
    }

    /** @return The {@link ZooKeeperConnection} instance used by this registry. */
    @VisibleForTesting
    ZooKeeperConnection getZooKeeperConnection() {
        return _zooKeeperConnection;
    }

    @VisibleForTesting
    String getRegisteredEndPointPath(ServiceEndPoint endPoint) {
        String path = makeEndPointPath(endPoint);
        ZooKeeperPersistentEphemeralNode node = _nodes.get(path);
        return (node != null) ? node.getActualPath() : null;
    }

    /**
     * Construct the path in ZooKeeper to where a service's children live.
     * @param serviceName The name of the service to get the ZooKeeper path for.
     * @return The ZooKeeper path.
     */
    public static String makeServicePath(String serviceName) {
        checkNotNull(serviceName);
        checkArgument(!"".equals(serviceName));
        return ZKPaths.makePath(ROOT_SERVICES_PATH, serviceName);<commit_msg>Added metrics deregistration to ZooKeeperServiceRegistry<commit_after>package com.bazaarvoice.soa.registry;

import com.bazaarvoice.soa.ServiceEndPoint;
import com.bazaarvoice.soa.ServiceEndPointJsonCodec;
import com.bazaarvoice.soa.ServiceRegistry;
import com.bazaarvoice.soa.metrics.UniqueMetricSource;
import com.bazaarvoice.zookeeper.ZooKeeperConnection;
import com.bazaarvoice.zookeeper.recipes.ZooKeeperPersistentEphemeralNode;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.netflix.curator.utils.ZKPaths;
import com.yammer.metrics.core.Gauge;
import org.apache.zookeeper.CreateMode;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * A <code>ServiceRegistry</code> implementation that uses ZooKeeper as its backing data store.
 */
public class ZooKeeperServiceRegistry implements ServiceRegistry
{
    /** The root path in ZooKeeper for where service registrations are stored. */
    @VisibleForTesting
    static final String ROOT_SERVICES_PATH = "ostrich";

    /** Maximum number of bytes that can be stored on a node in ZooKeeper. */
    @VisibleForTesting
    static final int MAX_DATA_SIZE = 1024 * 1024;

    // All dates are represented in ISO-8601 format and in the UTC time zone.
    @VisibleForTesting
    static final DateTimeFormatter ISO8601 = ISODateTimeFormat.dateTime().withZoneUTC();

    private final ZooKeeperConnection _zooKeeperConnection;
    private volatile boolean _closed = false;

    /** The ephemeral data that's been written to ZooKeeper.  Saved in case the connection is lost and then regained. */
    private final Map<String, ZooKeeperPersistentEphemeralNode> _nodes = Maps.newConcurrentMap();

    private final UniqueMetricSource _metricSource = new UniqueMetricSource(ZooKeeperServiceRegistry.class);

    public ZooKeeperServiceRegistry(ZooKeeperConnection connection) {
        checkNotNull(connection);
        _zooKeeperConnection = connection;
        _metricSource.newGauge("registered-end-points", new Gauge<Integer>() {
            @Override
            public Integer value() {
                return _nodes.size();
            }
        });
    }

    /** {@inheritDoc} */
    @Override
    public void register(ServiceEndPoint endPoint) {
        register(endPoint, true);
    }

    @VisibleForTesting
    void register(ServiceEndPoint endPoint, boolean includeRegistrationTime) {
        checkState(!_closed);
        checkNotNull(endPoint);

        Map<String, Object> registrationData = Maps.newHashMap();
        if (includeRegistrationTime) {
            DateTime now = DateTime.now().toDateTime(DateTimeZone.UTC);
            registrationData.put("registration-time", ISO8601.print(now));
        }

        byte[] data = ServiceEndPointJsonCodec.toJson(endPoint, registrationData).getBytes(Charsets.UTF_8);
        checkState(data.length < MAX_DATA_SIZE, "Serialized form of ServiceEndPoint must be < 1MB.");

        String path = makeEndPointPath(endPoint);
        _nodes.put(path, new ZooKeeperPersistentEphemeralNode(_zooKeeperConnection, path, data, CreateMode.EPHEMERAL));
    }

    /** {@inheritDoc} */
    @Override
    public void unregister(ServiceEndPoint endPoint) {
        checkState(!_closed);
        checkNotNull(endPoint);

        String path = makeEndPointPath(endPoint);
        ZooKeeperPersistentEphemeralNode node = _nodes.remove(path);
        if (node != null) {
            node.close(10, TimeUnit.SECONDS);
        }
    }

    @Override
    public synchronized void close() throws IOException {
        if (_closed) {
            return;
        }

        _closed = true;

        for (ZooKeeperPersistentEphemeralNode node : _nodes.values()) {
            node.close(10, TimeUnit.SECONDS);
        }
        _nodes.clear();
        _metricSource.close();
    }

    /** @return The {@link ZooKeeperConnection} instance used by this registry. */
    @VisibleForTesting
    ZooKeeperConnection getZooKeeperConnection() {
        return _zooKeeperConnection;
    }

    @VisibleForTesting
    String getRegisteredEndPointPath(ServiceEndPoint endPoint) {
        String path = makeEndPointPath(endPoint);
        ZooKeeperPersistentEphemeralNode node = _nodes.get(path);
        return (node != null) ? node.getActualPath() : null;
    }

    /**
     * Construct the path in ZooKeeper to where a service's children live.
     * @param serviceName The name of the service to get the ZooKeeper path for.
     * @return The ZooKeeper path.
     */
    public static String makeServicePath(String serviceName) {
        checkNotNull(serviceName);
        checkArgument(!"".equals(serviceName));
        return ZKPaths.makePath(ROOT_SERVICES_PATH, serviceName);
{'fraction_non_alphanumeric': 0.08955807587016035, 'fraction_numerical': 0.005084082909659757, 'mean_word_length': 3.520106053910738, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2895594', 'n_tokens_mistral': 3043, 'n_tokens_neox': 2853, 'n_words': 662}