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

com.github.richardballard.arbeeutils.function.RefreshableMemoisingSupplier Maven / Gradle / Ivy

There is a newer version: 2.0
Show newest version
/*
 * (C) Copyright 2016 Richard Ballard.
 *
 * 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 com.github.richardballard.arbeeutils.function;

import net.jcip.annotations.Immutable;
import net.jcip.annotations.ThreadSafe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Supplier;

/**
 * This class is an instance of {@link Supplier} that memoises the element.  That is to say, it has a delegate from
 * which it initially loads the element, and from this point on the {@link #get()} method returns that stored element.
 * It has the {@link #invalidate()} method that forces a reload from the delegate on the next call to {@link #get()}.
 */
@ThreadSafe
public class RefreshableMemoisingSupplier implements Supplier {

    @NotNull
    private final Supplier delegate;

    @NotNull
    private final LongAdder counter;

    @NotNull
    private volatile Optional> loadedElement;

    public RefreshableMemoisingSupplier(@NotNull final Supplier delegate) {
        assert delegate != null;

        this.delegate = delegate;

        this.counter = new LongAdder();
        this.loadedElement = Optional.empty();
    }

    /**
     * Returns the memoised element, loading it if necessary.
     */
    @Override
    public T get() {

        // Take a snapshot of the counter.  This way if another thread increments the counter while we are loading from
        // the delegate then the next time this method is called the counter will not match and so the memoised element
        // will not be used - RMB 2016/06/17
        final long counterSnapshot = counter.sum();

        // if the counter is still the same as when the element was loaded
        final Optional optionalElement = loadedElement.filter(le -> le.getLoadedOnCount() == counterSnapshot)
                                                         .map(LoadedElement::getElement);

        final T element;
        if(optionalElement.isPresent()) {
            element = optionalElement.get();
        }
        else {
            // load from the delegate
            element = delegate.get();

            loadedElement = Optional.of(new LoadedElement<>(element,
                                                            counterSnapshot));
        }

        return element;
    }

    /**
     * Invalidates any element that is memoised so that the next call to {@link #get()} will pull the element from
     * the delegate.
     */
    public void invalidate() {
        counter.increment();
    }

    /**
     * Convenience method that calls {@link #invalidate()} and then returns the result of {@link #get()}
     */
    public T refresh() {
        invalidate();
        return get();
    }

    @Immutable
    private static class LoadedElement {
        @Nullable
        private final T element;

        private final long loadedOnCount;

        public LoadedElement(@Nullable final T element,
                             final long loadedOnCount) {

            this.element = element;
            this.loadedOnCount = loadedOnCount;
        }

        @Nullable
        public T getElement() {
            return element;
        }

        public long getLoadedOnCount() {
            return loadedOnCount;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy