com.netflix.curator.x.discovery.details.ServiceCacheImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.curator.x.discovery.details.ServiceCacheImpl.java

Source

/*
 * Copyright 2012 Netflix, 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.netflix.curator.x.discovery.details;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.listen.ListenerContainer;
import com.netflix.curator.framework.recipes.cache.ChildData;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
import com.netflix.curator.utils.ZKPaths;
import com.netflix.curator.x.discovery.ServiceCache;
import com.netflix.curator.x.discovery.ServiceInstance;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;

public class ServiceCacheImpl<T> implements ServiceCache<T>, PathChildrenCacheListener {
    private final ListenerContainer<ServiceCacheListener> listenerContainer = new ListenerContainer<ServiceCacheListener>();
    private final ServiceDiscoveryImpl<T> discovery;
    private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT);
    private final PathChildrenCache cache;
    private final ConcurrentMap<String, ServiceInstance<T>> instances = Maps.newConcurrentMap();

    private enum State {
        LATENT, STARTED, STOPPED
    }

    ServiceCacheImpl(ServiceDiscoveryImpl<T> discovery, String name, ThreadFactory threadFactory) {
        Preconditions.checkNotNull(threadFactory, "threadFactory cannot be null");

        this.discovery = discovery;

        cache = new PathChildrenCache(discovery.getClient(), discovery.pathForName(name), true, threadFactory);
        cache.getListenable().addListener(this);
    }

    @Override
    public List<ServiceInstance<T>> getInstances() {
        return Lists.newArrayList(instances.values());
    }

    @Override
    public void start() throws Exception {
        Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED),
                "Cannot be started more than once");

        cache.start(true);
        for (ChildData childData : cache.getCurrentData()) {
            addInstance(childData, true);
        }
        discovery.cacheOpened(this);
    }

    @Override
    public void close() throws IOException {
        Preconditions.checkState(state.compareAndSet(State.STARTED, State.STOPPED),
                "Already closed or has not been started");

        listenerContainer.forEach(new Function<ServiceCacheListener, Void>() {
            @Override
            public Void apply(ServiceCacheListener listener) {
                discovery.getClient().getConnectionStateListenable().removeListener(listener);
                return null;
            }
        });
        listenerContainer.clear();

        Closeables.closeQuietly(cache);

        discovery.cacheClosed(this);
    }

    @Override
    public void addListener(ServiceCacheListener listener) {
        listenerContainer.addListener(listener);
        discovery.getClient().getConnectionStateListenable().addListener(listener);
    }

    @Override
    public void addListener(ServiceCacheListener listener, Executor executor) {
        listenerContainer.addListener(listener, executor);
        discovery.getClient().getConnectionStateListenable().addListener(listener, executor);
    }

    @Override
    public void removeListener(ServiceCacheListener listener) {
        listenerContainer.removeListener(listener);
        discovery.getClient().getConnectionStateListenable().removeListener(listener);
    }

    @Override
    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
        boolean notifyListeners = false;
        switch (event.getType()) {
        case CHILD_ADDED:
        case CHILD_UPDATED: {
            addInstance(event.getData(), false);
            notifyListeners = true;
            break;
        }

        case CHILD_REMOVED: {
            instances.remove(instanceIdFromData(event.getData()));
            notifyListeners = true;
            break;
        }
        }

        if (notifyListeners) {
            listenerContainer.forEach(new Function<ServiceCacheListener, Void>() {
                @Override
                public Void apply(ServiceCacheListener listener) {
                    listener.cacheChanged();
                    return null;
                }
            });
        }
    }

    private String instanceIdFromData(ChildData childData) {
        return ZKPaths.getNodeFromPath(childData.getPath());
    }

    private void addInstance(ChildData childData, boolean onlyIfAbsent) throws Exception {
        String instanceId = instanceIdFromData(childData);
        ServiceInstance<T> serviceInstance = discovery.getSerializer().deserialize(childData.getData());
        if (onlyIfAbsent) {
            instances.putIfAbsent(instanceId, serviceInstance);
        } else {
            instances.put(instanceId, serviceInstance);
        }
        cache.clearDataBytes(childData.getPath(), childData.getStat().getVersion());
    }
}