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

org.springframework.data.aerospike.cache.AerospikeCache Maven / Gradle / Ivy

There is a newer version: 4.8.0
Show newest version
/*
 * Copyright 2019 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
 *
 *     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 org.springframework.data.aerospike.cache;

import com.aerospike.client.IAerospikeClient;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
import com.aerospike.client.policy.RecordExistsAction;
import com.aerospike.client.policy.WritePolicy;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.data.aerospike.convert.AerospikeConverter;
import org.springframework.data.aerospike.convert.AerospikeReadData;
import org.springframework.data.aerospike.convert.AerospikeWriteData;
import org.springframework.data.aerospike.core.WritePolicyBuilder;

import java.util.Objects;
import java.util.concurrent.Callable;

/**
 * A Cache {@link org.springframework.cache.Cache} implementation backed by Aerospike database as store. Create and
 * configure Aerospike cache instances via {@link AerospikeCacheManager}.
 *
 * @author Venil Noronha
 */
public class AerospikeCache implements Cache {

    private static final String VALUE = "value";

    private final String name;
    private final IAerospikeClient client;
    private final AerospikeConverter aerospikeConverter;
    private final AerospikeCacheConfiguration cacheConfiguration;
    private final WritePolicy createOnly;
    private final WritePolicy writePolicyForPut;

    public AerospikeCache(String name,
                          IAerospikeClient client,
                          AerospikeConverter aerospikeConverter,
                          AerospikeCacheConfiguration cacheConfiguration) {
        this.name = name;
        this.client = client;
        this.aerospikeConverter = aerospikeConverter;
        this.cacheConfiguration = cacheConfiguration;
        this.createOnly = WritePolicyBuilder.builder(client.getWritePolicyDefault())
            .recordExistsAction(RecordExistsAction.CREATE_ONLY)
            .expiration(cacheConfiguration.getExpirationInSeconds())
            .build();
        this.writePolicyForPut = WritePolicyBuilder.builder(client.getWritePolicyDefault())
            .expiration(cacheConfiguration.getExpirationInSeconds())
            .build();
    }

    /**
     * Clears the cache by truncating the configured cache's set (in the configured namespace).
     */
    @Override
    public void clear() {
        client.truncate(null, cacheConfiguration.getNamespace(), cacheConfiguration.getSet(), null);
    }

    /**
     * Deletes the key from Aerospike database.
     *
     * @param key The key to delete.
     */
    @Override
    public void evict(Object key) {
        client.delete(null, getKey(key));
    }

    /**
     * Get cache's name.
     *
     * @return The cache's name.
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * Get the underlying native cache provider - the Aerospike client.
     *
     * @return The aerospike client.
     */
    @Override
    public Object getNativeCache() {
        return client;
    }

    /**
     * Return the value (bins) from the Aerospike database to which this cache maps the specified key, obtaining that
     * value from valueLoader if necessary. This method provides a simple substitute for the conventional "if cached,
     * return; otherwise create, cache and return" pattern.
     *
     * @param key         The key whose associated value is to be returned.
     * @param valueLoader The value loader that might contain the value (bins).
     * @return The value (bins) to which this cache maps the specified key.
     */
    @Override
    @SuppressWarnings({"unchecked", "NullableProblems"})
    public  T get(Object key, Callable valueLoader) {
        T value = (T) client.get(null, getKey(key)).getValue(VALUE);
        if (Objects.isNull(value)) {
            try {
                value = valueLoader.call();
                if (Objects.nonNull(value)) {
                    put(key, value);
                }
            } catch (Exception e) {
                throw new Cache.ValueRetrievalException(key, valueLoader, e);
            }
        }
        return value;
    }

    /**
     * Return the value (bins) from the Aerospike database to which this cache maps the specified key. Generically
     * specifying a type that return value will be cast to.
     *
     * @param key  The key whose associated value (bins) is to be returned.
     * @param type The required type of the returned value (can be null to bypass a type check; in case of a null value
     *             found in the cache, the specified type is irrelevant).
     * @return The value (bins) to which this cache maps the specified key (which may be null itself), or also null if
     * the cache contains no mapping for this key.
     */
    @Override
    public  T get(Object key, Class type) {
        Key dbKey = getKey(key);
        Record aeroRecord = client.get(null, dbKey);
        if (aeroRecord != null) {
            AerospikeReadData data = AerospikeReadData.forRead(dbKey, aeroRecord);
            return aerospikeConverter.read(type, data);
        }
        return null;
    }

    /**
     * Returns the value (bins) from the Aerospike database to which this cache maps the specified key. Returns null if
     * the cache contains no mapping for this key; otherwise, the cached value (which may be null itself) will be
     * returned in a Cache.ValueWrapper.
     *
     * @param key The key whose associated value (bins) is to be returned.
     * @return The value (bins) to which this cache maps the specified key, contained within a Cache.ValueWrapper which
     * may also hold a cached null value. A straight null being returned means that the cache contains no mapping for
     * this key.
     */
    @Override
    public ValueWrapper get(Object key) {
        Object value = get(key, Object.class);
        return (value != null ? new SimpleValueWrapper(value) : null);
    }

    /**
     * Write the key-value pair to Aerospike database.
     *
     * @param key   The key to write.
     * @param value The value to write.
     */
    @Override
    public void put(Object key, Object value) {
        serializeAndPut(writePolicyForPut, key, value);
    }

    /**
     * Write the key-value pair to Aerospike database if the key doesn't already exist.
     *
     * @param key   The key to write.
     * @param value The value (bins) to write.
     * @return In case the key already exists return the existing value, else return null.
     */
    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        ValueWrapper valueWrapper = get(key);
        // Key already exists, return the existing value
        if (valueWrapper != null) {
            return valueWrapper;
        }
        // Key doesn't exist, write the new given key-value to Aerospike database and return null
        serializeAndPut(createOnly, key, value);
        return null;
    }

    private Key getKey(Object key) {
        return new Key(cacheConfiguration.getNamespace(), cacheConfiguration.getSet(), key.toString());
    }

    private void serializeAndPut(WritePolicy writePolicy, Object key, Object value) {
        AerospikeWriteData data = AerospikeWriteData.forWrite(getKey(key).namespace);
        aerospikeConverter.write(value, data);
        client.put(writePolicy, getKey(key), data.getBinsAsArray());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy