com.google.appengine.api.memcache.stdimpl.GCache Maven / Gradle / Ivy
/*
* Copyright 2021 Google LLC
*
* 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
*
* https://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.google.appengine.api.memcache.stdimpl;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.memcache.Stats;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.cache.Cache;
import javax.cache.CacheEntry;
import javax.cache.CacheListener;
import javax.cache.CacheStatistics;
/**
* JCache Cache implementation using Memcache.
*
*/
public class GCache implements Cache {
private final MemcacheService service;
private final List listeners;
private final Expiration expiration;
private final MemcacheService.SetPolicy setPolicy;
/**
* Creates a JCache implementation over the provided service with the given properties.
*
* @param properties Properties for this cache.
*/
public GCache(Map, ?> properties) {
listeners = new LinkedList();
if (properties != null) {
// Expiration in seconds.
Object expirationProperty = properties.get(GCacheFactory.EXPIRATION_DELTA);
int millis = 0;
if (expirationProperty instanceof Integer) {
millis = (Integer) expirationProperty * 1000;
}
// Expiration in milliseconds.
expirationProperty = properties.get(GCacheFactory.EXPIRATION_DELTA_MILLIS);
if (expirationProperty instanceof Integer) {
millis = (Integer) expirationProperty;
}
if (millis != 0) {
expiration = Expiration.byDeltaMillis(millis);
} else {
expirationProperty = properties.get(GCacheFactory.EXPIRATION);
if (expirationProperty instanceof Date) {
expiration = Expiration.onDate((Date) expirationProperty);
} else {
expiration = null;
}
}
Object setProperty = properties.get(GCacheFactory.SET_POLICY);
if (setProperty instanceof MemcacheService.SetPolicy) {
setPolicy = (MemcacheService.SetPolicy) setProperty;
} else {
setPolicy = MemcacheService.SetPolicy.SET_ALWAYS;
}
Object memcacheService = properties.get(GCacheFactory.MEMCACHE_SERVICE);
if (memcacheService instanceof MemcacheService) {
this.service = (MemcacheService) memcacheService;
} else {
Object namespace = properties.get(GCacheFactory.NAMESPACE);
if (!(namespace instanceof String)) {
namespace = null;
}
this.service = MemcacheServiceFactory.getMemcacheService((String) namespace);
}
} else {
expiration = null;
setPolicy = MemcacheService.SetPolicy.SET_ALWAYS;
this.service = MemcacheServiceFactory.getMemcacheService();
}
}
@Override
public void addListener(CacheListener cacheListener) {
listeners.add(cacheListener);
}
@Override
public void evict() {
// Do nothing. Expiration based eviction happens without user input.
}
@Override
@SuppressWarnings("rawtypes")
public Map getAll(Collection collection) {
Collection> collection1 = collection;
return service.getAll(collection1);
}
@Override
public CacheEntry getCacheEntry(Object o) {
Object value = service.get(o);
if (value == null && !service.contains(o)) {
return null;
}
return new GCacheEntry(this, o, value);
}
@Override
public CacheStatistics getCacheStatistics() {
return new GCacheStats(service.getStatistics());
}
/** Not supported. */
@Override
public void load(Object o) {
// We don't support asynchronous cache loading.
throw new UnsupportedOperationException();
}
/** Not supported. */
@SuppressWarnings("rawtypes")
@Override
public void loadAll(Collection collection) {
// We don't support asynchronous cache loading.
throw new UnsupportedOperationException();
}
@Override
public Object peek(Object o) {
return get(o);
}
@Override
public void removeListener(CacheListener cacheListener) {
listeners.remove(cacheListener);
}
@Override
public int size() {
return getCacheStatistics().getObjectCount();
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean containsKey(Object key) {
return service.contains(key);
}
/** Not supported. */
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public Object get(Object key) {
return service.get(key);
}
@Override
public Object put(Object key, Object value) {
for (CacheListener listener : listeners) {
listener.onPut(value);
}
if (!service.put(key, value, expiration, setPolicy)) {
throw new GCacheException("Policy prevented put operation");
}
return null;
}
@Override
public Object remove(Object key) {
for (CacheListener listener : listeners) {
listener.onRmove(key);
}
Object value = service.get(key);
service.delete(key);
return value;
}
@SuppressWarnings("rawtypes")
@Override
public void putAll(Map m) {
@SuppressWarnings("unchecked")
Set> added = service.putAll(m, expiration, setPolicy);
added.removeAll(m.keySet());
if (!added.isEmpty()) {
throw new GCacheException("Policy prevented some put operations");
}
}
@Override
public void clear() {
for (CacheListener listener : listeners) {
listener.onClear();
}
service.clearAll();
}
/** Not supported. */
@SuppressWarnings("rawtypes")
@Override
public Set keySet() {
throw new UnsupportedOperationException();
}
/** Not supported. */
@SuppressWarnings("rawtypes")
@Override
public Collection values() {
throw new UnsupportedOperationException();
}
/** Not supported. */
@SuppressWarnings("rawtypes")
@Override
public Set entrySet() {
throw new UnsupportedOperationException();
}
/** Implementation of the JCache {@link CacheStatistics} using memcache provided statistics. */
private static class GCacheStats implements CacheStatistics {
private Stats stats;
/**
* Creates a statistics snapshot using the provided stats.
*
* @param stats Statistics to use.
*/
private GCacheStats(Stats stats) {
this.stats = stats;
}
@Override
public void clearStatistics() {
throw new UnsupportedOperationException();
}
@Override
public int getCacheHits() {
return (int) stats.getHitCount();
}
@Override
public int getCacheMisses() {
return (int) stats.getMissCount();
}
@Override
public int getObjectCount() {
return (int) stats.getItemCount();
}
@Override
public int getStatisticsAccuracy() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return stats.toString();
}
}
}