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

com.netflix.ribbon.evache.EvCacheProvider Maven / Gradle / Ivy

There is a newer version: 2.7.18
Show newest version
/*
 * Copyright 2014 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.ribbon.evache;

import com.netflix.evcache.EVCache;
import com.netflix.evcache.EVCacheException;
import com.netflix.ribbon.CacheProvider;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @author Tomasz Bak
 */
public class EvCacheProvider implements CacheProvider {

    private static final Logger LOGGER = LoggerFactory.getLogger(EvCacheProvider.class);

    private static final long WATCH_INTERVAL = 1;

    private static final FutureObserver FUTURE_OBSERVER;

    static {
        FUTURE_OBSERVER = new FutureObserver();
        FUTURE_OBSERVER.start();
    }

    private final EvCacheOptions options;
    private final EVCache evCache;

    public EvCacheProvider(EvCacheOptions options) {
        this.options = options;
        EVCache.Builder builder = new EVCache.Builder();
        if (options.isEnableZoneFallback()) {
            builder.enableZoneFallback();
        }
        builder.setDefaultTTL(options.getTimeToLive());
        builder.setAppName(options.getAppName());
        builder.setCacheName(options.getCacheName());
        evCache = builder.build();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Observable get(final String key, Map requestProperties) {
        return Observable.create(new OnSubscribe() {
            @Override
            public void call(Subscriber subscriber) {
                Future getFuture;
                try {
                    if (options.getTranscoder() == null) {
                        getFuture = evCache.getAsynchronous(key);
                    } else {
                        getFuture = (Future) evCache.getAsynchronous(key, options.getTranscoder());
                    }
                    FUTURE_OBSERVER.watchFuture(getFuture, subscriber);
                } catch (EVCacheException e) {
                    subscriber.onError(new CacheFaultException("EVCache exception when getting value for key " + key, e));
                }
            }
        });
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    static final class FutureObserver extends Thread {
        private final Map futureMap = new ConcurrentHashMap();

        FutureObserver() {
            super("EvCache-Future-Observer");
            setDaemon(true);
        }

        @Override
        public void run() {
            while (true) {
                for (Map.Entry f : futureMap.entrySet()) {
                    Future future = f.getKey();
                    Subscriber subscriber = f.getValue();
                    if (subscriber.isUnsubscribed()) {
                        future.cancel(true);
                        futureMap.remove(future);
                    } else if (future.isDone()) {
                        try {
                            handleCompletedFuture(future, subscriber);
                        } catch (Error e) {
                            throw e;
                        } catch (Throwable e) {
                            LOGGER.warn("unexpected error during checking future result", e);
                        } finally {
                            futureMap.remove(future);
                        }
                    }
                }

                try {
                    Thread.sleep(WATCH_INTERVAL);
                } catch (InterruptedException e) {
                    // Never terminate
                }
            }
        }

        private static void handleCompletedFuture(Future future, Subscriber subscriber) throws InterruptedException {
            if (future.isCancelled()) {
                subscriber.onError(new CacheFaultException("cache get request canceled"));
            } else {
                try {
                    Object value = future.get();
                    if (value == null) {
                        subscriber.onError(new CacheMissException());
                    } else {
                        subscriber.onNext(value);
                        subscriber.onCompleted();
                    }
                } catch (ExecutionException e) {
                    subscriber.onError(e.getCause());
                }
            }
        }

        void watchFuture(Future future, Subscriber subscriber) {
            futureMap.put(future, subscriber);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy