/* * Copyright (c) 2014 the original author or authors * * 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 io.werval.spi.cache; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; import io.werval.api.cache.Cache; /** * Cache Adapter. * <p> * Default behaviour and Metrics handling (hits, misses, updates and removes meters). */ public abstract class CacheAdapter implements Cache { /** * Cache Event. * <p> * Used to collect metrics. * * @hidden */ public static enum CacheEvent { HIT, MISS, GET, SET, REMOVE; /** * Close metric recording. * * @hidden */ public interface Closeable extends AutoCloseable { @Override void close(); } /** * No-operation closeable. */ public static final Closeable NOOP_CLOSEABLE = new Closeable() { @Override public void close() { } }; /** * No-operation metrics handler. */ public static final Function<CacheEvent, Closeable> NOOP_HANDLER = new Function<CacheEvent, Closeable>() { @Override public Closeable apply( CacheEvent event ) { return NOOP_CLOSEABLE; } }; } private final Function<CacheEvent, CacheEvent.Closeable> handler; /** * Create a Cache without metrics handling. */ protected CacheAdapter() { this( null ); } /** * Create a Cache with metrics handling. * * @param handler Metrics handler */ protected CacheAdapter( Function<CacheEvent, CacheEvent.Closeable> handler ) { this.handler = handler == null ? CacheEvent.NOOP_HANDLER : handler; } @Override public final boolean has( String key ) { return get( key ) != null; } @Override public final <T> T get( String key ) { try( CacheEvent.Closeable getTimer = handler.apply( CacheEvent.GET ) ) { T value = doGet( key ); handler.apply( value == null ? CacheEvent.MISS : CacheEvent.HIT ).close(); return value; } } @Override public final <T> Optional<T> getOptional( String key ) { return Optional.ofNullable( this.<T>get( key ) ); } @Override public final <T> T getOrSetDefault( String key, T defaultValue ) { return getOrSetDefault( key, 0, defaultValue ); } @Override public final <T> T getOrSetDefault( String key, Supplier<T> defaultValueSupplier ) { return getOrSetDefault( key, 0, defaultValueSupplier.get() ); } @Override public final <T> T getOrSetDefault( String key, int ttlSeconds, T defaultValue ) { T value = this.<T>get( key ); if( value == null ) { set( ttlSeconds, key, defaultValue ); return defaultValue; } return value; } @Override public final <T> T getOrSetDefault( String key, int ttlSeconds, Supplier<T> defaultValueSupplier ) { return getOrSetDefault( key, ttlSeconds, defaultValueSupplier.get() ); } @Override public final <T> void set( String key, T value ) { set( 0, key, value ); } @Override public final <T> void set( int ttlSeconds, String key, T value ) { try( CacheEvent.Closeable setTimer = handler.apply( CacheEvent.SET ) ) { doSet( ttlSeconds, key, value ); } } @Override public final void remove( String key ) { try( CacheEvent.Closeable removeTimer = handler.apply( CacheEvent.REMOVE ) ) { doRemove( key ); } } /** * Concrete GET. * * @param <T> Parameterized type of value * @param key Cache Key * * @return Value or null */ protected abstract <T> T doGet( String key ); /** * Concrete SET. * * @param <T> Parameterized type of value * @param ttlSeconds TTL in seconds * @param key Cache Key * @param value Value */ protected abstract <T> void doSet( int ttlSeconds, String key, T value ); /** * Concrete REMOVE. * * @param key Cache Key */ protected abstract void doRemove( String key ); }