Package org.auraframework.impl.cache

Source Code of org.auraframework.impl.cache.CacheImpl$Builder

/*
* Copyright (C) 2013 salesforce.com, 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 org.auraframework.impl.cache;

import java.util.ArrayList;
import java.util.Set;

import org.apache.log4j.Logger;
import org.auraframework.cache.Cache;
import org.auraframework.impl.adapter.ConfigAdapterImpl;

import com.google.common.cache.CacheStats;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

public class CacheImpl<K, T> implements Cache<K, T> {

    private static class EvictionListener<K, T> implements RemovalListener<K, T> {

        /** A default name string */
        private static final String UNNAMED = "(unnamed)";

        /** Interval at which to log cache stats in "normal" operation */
        private static final long ONE_DAY = 1000 * 60 * 60 * 24;

        /** A name for the cache being listened to, to clarifiy in logs which one evicted */
        private final String name;

        /** The cache for this listener, to fetch statistics. */
        private com.google.common.cache.Cache<K, T> cache;

        /** Count of log-worth evictions, to avoid spamming the log*/
        private int evictions = 0;

        /** Log threshold for next actual emission to logs */
        private int nextLogThreshold = 1;

        /** Log the entire stats once a day, regardless of evictions. */
        private long lastFull = System.currentTimeMillis();
        EvictionListener(String name) {
            this.name = name == null ? UNNAMED : name;
        }

        void setCache(com.google.common.cache.Cache<K, T> cache) {
            this.cache = cache;
        }

        @Override
        public void onRemoval(RemovalNotification<K, T> notification) {
            // We don't much care about removal for reasons other than space constraint;
            // those happen for lots of reasons.  But size eviction means size pressure,
            // which we do care about.
            if (notification.getCause() == RemovalCause.SIZE) {
                evictions++;
                if (evictions >= nextLogThreshold) {
                    Logger logger = Logger.getLogger(ConfigAdapterImpl.class);
                    CacheStats stats = cache.stats();
                    logger.info(String.format(
                        "Cache %s evicted %d entries for size pressure, hit rate=%.3f, evictions=%d, loads=%d %s",
                        name, evictions, stats.hitRate(), stats.evictionCount(),
                        stats.loadCount(), stats.toString()));
                    // We want to log every 10 until 100, every 100 until 1000, every 1000 thereafter
                    if (nextLogThreshold == 1) {
                        nextLogThreshold = 10;
                    } else if (nextLogThreshold < 100) {
                        nextLogThreshold += 10;
                    } else if (nextLogThreshold < 1000) {
                        nextLogThreshold += 100;
                    } else {
                        nextLogThreshold += 1000;
                    }
                }
            }
            if (System.currentTimeMillis() >= lastFull + ONE_DAY) {
                Logger logger = Logger.getLogger(ConfigAdapterImpl.class);
                CacheStats stats = cache.stats();
                logger.info(String.format("Cache %s has hit rate=%.3f, stats %s\n",
                        name, stats.hitRate(), stats.toString()));
            }
        }
    };

  private com.google.common.cache.Cache<K, T> cache;

  CacheImpl(com.google.common.cache.Cache<K, T> cache) {
    this.cache = cache;
  }

  public CacheImpl(Builder<K, T> builder) {
    // if builder.useSecondaryStorage is true, we should try to use a
    // non-quava secondary-storage cache with streaming ability

    com.google.common.cache.CacheBuilder<Object, Object> cb = com.google.common.cache.CacheBuilder
        .newBuilder().initialCapacity(builder.initialCapacity)
        .maximumSize(builder.maximumSize)
        .concurrencyLevel(builder.concurrencyLevel);

    if (builder.recordStats) {
      cb = cb.recordStats();
    }

    if (builder.softValues) {
      cb = cb.softValues();
    }

        EvictionListener<K, T> listener = new EvictionListener<K, T>(builder.name);
    cb.removalListener(listener);
    cache = cb.build();
    listener.setCache(cache);
  }

  @Override
  public T getIfPresent(K key) {
    return cache.getIfPresent(key);
  }

  @Override
  public void put(K key, T data) {
    cache.put(key, data);

  }

  @Override
  public void invalidate(K key) {
    cache.invalidate(key);

  }

  @Override
  public void invalidate(Iterable<K> keys) {
    cache.invalidate(keys);
  }

  @Override
  public void invalidateAll() {
    cache.invalidateAll();
  }

  @Override
  public Set<K> getKeySet() {
    return cache.asMap().keySet();
  }

  @Override
  public void invalidatePartial(String keyBeginsWith) {
    // everything is a match if the match length is zero
    if (keyBeginsWith == null || keyBeginsWith.length() == 0) {
      invalidateAll();
      return;
    }

    // add beginsWith matches to invalidItems
    Set<K> set = getKeySet();
    ArrayList<K> invalidItems = new ArrayList<K>();
    for (K key : set) {
      if (key.toString().startsWith(keyBeginsWith)) {
        invalidItems.add(key);
      }
    }

    // invalidate collected items
    if (!invalidItems.isEmpty()) {
      cache.invalidate(invalidItems);
    }
  }

  @Override
  public Object getPrivateUnderlyingCache() {
    return cache;
  }

  public static class Builder<K, T> implements
      org.auraframework.builder.CacheBuilder<K, T> {
    // builder defaults
        int initialCapacity = 128;
        int concurrencyLevel = 4;
        long maximumSize = 1024;
        boolean recordStats = false;
        boolean softValues = true;
        boolean useSecondaryStorage = false;
        String name;

    public Builder() {

    }

    @Override
    public Builder<K, T> setInitialSize(int initialCapacity) {
      this.initialCapacity = initialCapacity;
      return this;
    };

    @Override
    public Builder<K, T> setMaximumSize(long maximumSize) {
      this.maximumSize = maximumSize;
      return this;
    };

    @Override
    public Builder<K, T> setUseSecondaryStorage(boolean useSecondaryStorage) {
      this.useSecondaryStorage = useSecondaryStorage;
      return this;
    }

    @Override
    public Builder<K, T> setRecordStats(boolean recordStats) {
      this.recordStats = recordStats;
      return this;
    }

    @Override
    public Builder<K, T> setSoftValues(boolean softValues) {
      this.softValues = softValues;
      return this;
    }

    @Override
    public Builder<K, T> setConcurrencyLevel(int concurrencyLevel) {
      this.concurrencyLevel = concurrencyLevel;
      return this;
    }

        @Override
        public Builder<K, T> setName(String name) {
            this.name = name;
            return this;
        }

    @Override
    public CacheImpl<K, T> build() {
      return new CacheImpl<K, T>(this);
    }

  }

}
TOP

Related Classes of org.auraframework.impl.cache.CacheImpl$Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.