GuavaCacheAdapter.java
- /*
- * #%L
- * wcm.io
- * %%
- * Copyright (C) 2014 wcm.io
- * %%
- * 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.
- * #L%
- */
- package io.wcm.caravan.pipeline.cache.guava.impl;
- import io.wcm.caravan.pipeline.cache.CachePersistencyOptions;
- import io.wcm.caravan.pipeline.cache.spi.CacheAdapter;
- import java.math.BigDecimal;
- import java.util.Map;
- import org.apache.felix.scr.annotations.Activate;
- import org.apache.felix.scr.annotations.Component;
- import org.apache.felix.scr.annotations.Deactivate;
- import org.apache.felix.scr.annotations.Property;
- import org.apache.felix.scr.annotations.Reference;
- import org.apache.felix.scr.annotations.Service;
- import org.apache.sling.commons.osgi.PropertiesUtil;
- import org.osgi.framework.Constants;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import rx.Observable;
- import com.codahale.metrics.Counter;
- import com.codahale.metrics.MetricRegistry;
- import com.codahale.metrics.Timer;
- import com.google.common.cache.Cache;
- import com.google.common.cache.CacheBuilder;
- import com.google.common.cache.Weigher;
- /**
- * {@link CacheAdapter} implementation for Guava.
- * Provides guava {@link Cache}, which size is specified in bytes. Default cache size is 10 MB. Provide higher property
- * value {@value #MAX_CACHE_SIZE_MB_PROPERTY} to set up higher cache capacity.
- * items life time depends on the amount and size of stored cache items. Items, which capacity is higher than 1/4 of the
- * declared cache size will not be stored.
- */
- @Component(immediate = true, metatype = true,
- label = "wcm.io Caravan Pipeline Cache Adapter for Guava",
- description = "Configure pipeline caching in guava.")
- @Service(CacheAdapter.class)
- public class GuavaCacheAdapter implements CacheAdapter {
- private static final Logger log = LoggerFactory.getLogger(GuavaCacheAdapter.class);
- @Property(label = "Service Ranking", intValue = GuavaCacheAdapter.DEFAULT_RANKING,
- description = "Used to determine the order of caching layers if you are using multiple Cache Adapters. "
- + "Fast system-internal caches should have a lower service than slower network caches, so that they are queried first.",
- propertyPrivate = false)
- static final String PROPERTY_RANKING = Constants.SERVICE_RANKING;
- static final int DEFAULT_RANKING = 1000;
- @Property(label = "Max. size in MB",
- description = "Declares the maximum total amount of VM memory in Megabyte that will be used by this cache adapter.")
- static final String MAX_CACHE_SIZE_MB_PROPERTY = "maxCacheSizeMB";
- private static final Integer MAX_CACHE_SIZE_MB_DEFAULT = 10;
- @Property(label = "Enabled",
- description = "Enables or disables the whole cache adapter and all operations.",
- boolValue = GuavaCacheAdapter.CACHE_ENABLED_DEFAULT)
- static final String CACHE_ENABLED_PROPERTY = "enabled";
- private static final boolean CACHE_ENABLED_DEFAULT = true;
- /**
- * 1024*1024 multiplier used to provide bytes from megabyte values to create correct cache weight
- */
- private static final BigDecimal WEIGHT_MULTIPLIER = new BigDecimal(1048576);
- private Cache<String, String> guavaCache;
- private long cacheWeightInBytes;
- private boolean enabled;
- @Reference
- private MetricRegistry metricRegistry;
- private Timer getLatencyTimer;
- private Timer putLatencyTimer;
- private Counter hitsCounter;
- private Counter missesCounter;
- @Activate
- void activate(Map<String, Object> config) {
- cacheWeightInBytes = new BigDecimal(PropertiesUtil.toDouble(config.get(MAX_CACHE_SIZE_MB_PROPERTY), MAX_CACHE_SIZE_MB_DEFAULT))
- .multiply(WEIGHT_MULTIPLIER).longValue();
- this.guavaCache = CacheBuilder.newBuilder().weigher(new Weigher<String, String>() {
- @Override
- public int weigh(String key, String value) {
- return getWeight(key) + getWeight(value);
- }
- }).maximumWeight(cacheWeightInBytes).build();
- enabled = PropertiesUtil.toBoolean(config.get(CACHE_ENABLED_PROPERTY), CACHE_ENABLED_DEFAULT);
- getLatencyTimer = metricRegistry.timer(MetricRegistry.name(getClass(), "latency", "get"));
- putLatencyTimer = metricRegistry.timer(MetricRegistry.name(getClass(), "latency", "put"));
- hitsCounter = metricRegistry.counter(MetricRegistry.name(getClass(), "hits"));
- missesCounter = metricRegistry.counter(MetricRegistry.name(getClass(), "misses"));
- }
- private int getWeight(String toMeasure) {
- return 8 * ((((toMeasure.length()) * 2) + 45) / 8);
- }
- @Deactivate
- void deactivate() {
- metricRegistry.remove(MetricRegistry.name(getClass(), "latency", "get"));
- metricRegistry.remove(MetricRegistry.name(getClass(), "latency", "put"));
- metricRegistry.remove(MetricRegistry.name(getClass(), "hits"));
- metricRegistry.remove(MetricRegistry.name(getClass(), "misses"));
- }
- @Override
- public Observable<String> get(String cacheKey, CachePersistencyOptions options) {
- if (!enabled || !options.shouldUseTransientCaches()) {
- return Observable.empty();
- }
- return Observable.create(subscriber -> {
- Timer.Context context = getLatencyTimer.time();
- String cacheEntry = guavaCache.getIfPresent(cacheKey);
- if (cacheEntry != null) {
- hitsCounter.inc();
- subscriber.onNext(cacheEntry);
- }
- else {
- missesCounter.inc();
- }
- context.stop();
- log.trace("Succesfully retrieved document with id {}: {}", cacheKey, cacheEntry);
- subscriber.onCompleted();
- });
- }
- @Override
- public void put(String cacheKey, String jsonString, CachePersistencyOptions options) {
- if (!enabled || !options.shouldUseTransientCaches()) {
- return;
- }
- Timer.Context context = putLatencyTimer.time();
- guavaCache.put(cacheKey, jsonString);
- context.stop();
- log.trace("Succesfully put document into Guava cache with id {}:\n{}", cacheKey, jsonString);
- }
- }