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

software.amazon.awssdk.profiles.internal.ProfileFileRefresher Maven / Gradle / Ivy

Go to download

Profile module allows loading information from AWS configuration and credentials files.

There is a newer version: 2.28.5
Show newest version
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.profiles.internal;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Instant;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.utils.cache.CachedSupplier;
import software.amazon.awssdk.utils.cache.RefreshResult;

/**
 * Class used for caching and reloading ProfileFile objects from a Supplier.
 */
@SdkInternalApi
public final class ProfileFileRefresher {

    private static final ProfileFileRefreshRecord EMPTY_REFRESH_RECORD = ProfileFileRefreshRecord.builder()
                                                                                                 .refreshTime(Instant.MIN)
                                                                                                 .build();
    private final CachedSupplier profileFileCache;
    private volatile ProfileFileRefreshRecord currentRefreshRecord;
    private final Supplier profileFile;
    private final Path profileFilePath;
    private final Consumer onProfileFileReload;
    private final Clock clock;

    private ProfileFileRefresher(Builder builder) {
        this.clock = builder.clock;
        this.profileFile = builder.profileFile;
        this.profileFilePath = builder.profileFilePath;
        this.onProfileFileReload = builder.onProfileFileReload;
        this.profileFileCache = CachedSupplier.builder(this::refreshResult)
                                              .cachedValueName("ProfileFileSupplier()")
                                              .clock(this.clock)
                                              .build();
        this.currentRefreshRecord = EMPTY_REFRESH_RECORD;
    }

    /**
     * Builder method to construct instance of ProfileFileRefresher.
     */
    public static ProfileFileRefresher.Builder builder() {
        return new ProfileFileRefresher.Builder();
    }

    /**
     * Retrieves the cache value or refreshes it if stale.
     */
    public ProfileFile refreshIfStale() {
        ProfileFileRefreshRecord cachedOrRefreshedRecord = profileFileCache.get();
        ProfileFile cachedOrRefreshedProfileFile = cachedOrRefreshedRecord.profileFile;
        if (isNewProfileFile(cachedOrRefreshedProfileFile)) {
            currentRefreshRecord = cachedOrRefreshedRecord;
        }

        return cachedOrRefreshedProfileFile;
    }

    private RefreshResult refreshResult() {
        return reloadAsRefreshResultIfStale();
    }

    private RefreshResult reloadAsRefreshResultIfStale() {
        Instant now = clock.instant();
        ProfileFileRefreshRecord refreshRecord;

        if (canReloadProfileFile() || hasNotBeenPreviouslyLoaded()) {
            ProfileFile reloadedProfileFile = reload(profileFile, onProfileFileReload);
            refreshRecord = ProfileFileRefreshRecord.builder()
                                                    .profileFile(reloadedProfileFile)
                                                    .refreshTime(now)
                                                    .build();
        } else {
            refreshRecord = currentRefreshRecord;
        }

        return wrapIntoRefreshResult(refreshRecord, now);
    }

    private  RefreshResult wrapIntoRefreshResult(T value, Instant staleTime) {
        return RefreshResult.builder(value)
                            .staleTime(staleTime)
                            .build();
    }

    private static ProfileFile reload(Supplier supplier) {
        return supplier.get();
    }

    private static ProfileFile reload(Supplier supplier, Consumer consumer) {
        ProfileFile reloadedProfileFile = reload(supplier);
        consumer.accept(reloadedProfileFile);

        return reloadedProfileFile;
    }

    private boolean isNewProfileFile(ProfileFile profileFile) {
        return !Objects.equals(currentRefreshRecord.profileFile, profileFile);
    }

    private boolean canReloadProfileFile() {
        if (Objects.isNull(profileFilePath)) {
            return false;
        }

        try {
            Instant lastModifiedInstant = Files.getLastModifiedTime(profileFilePath).toInstant();
            return currentRefreshRecord.refreshTime.isBefore(lastModifiedInstant);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private boolean hasNotBeenPreviouslyLoaded() {
        return currentRefreshRecord == EMPTY_REFRESH_RECORD;
    }


    public static final class Builder {

        private Supplier profileFile;
        private Path profileFilePath;
        private Consumer onProfileFileReload = p -> { };
        private Clock clock = Clock.systemUTC();

        private Builder() {
        }

        public Builder profileFile(Supplier profileFile) {
            this.profileFile = profileFile;
            return this;
        }

        public Builder profileFilePath(Path profileFilePath) {
            this.profileFilePath = profileFilePath;
            return this;
        }

        /**
         * Sets a clock for managing stale and prefetch durations.
         */
        @SdkTestInternalApi
        public Builder clock(Clock clock) {
            this.clock = clock;
            return this;
        }

        /**
         * Sets a custom action to perform when a profile file is reloaded. This action is executed when both the cache is stale
         * and the disk file associated with the profile file has been modified since the last load.
         *
         * @param consumer The action to perform.
         */
        public Builder onProfileFileReload(Consumer consumer) {
            this.onProfileFileReload = consumer;
            return this;
        }

        public ProfileFileRefresher build() {
            return new ProfileFileRefresher(this);
        }
    }

    /**
     * Class used to encapsulate additional refresh information.
     */
    public static final class ProfileFileRefreshRecord {
        private final Instant refreshTime;
        private final ProfileFile profileFile;

        private ProfileFileRefreshRecord(Builder builder) {
            this.profileFile = builder.profileFile;
            this.refreshTime = builder.refreshTime;
        }

        /**
         * The refreshed ProfileFile instance.
         */
        public ProfileFile profileFile() {
            return profileFile;
        }

        /**
         * The time at which the RefreshResult was created.
         */
        public Instant refreshTime() {
            return refreshTime;
        }

        static Builder builder() {
            return new Builder();
        }

        private static final class Builder {
            private Instant refreshTime;
            private ProfileFile profileFile;

            Builder refreshTime(Instant refreshTime) {
                this.refreshTime = refreshTime;
                return this;
            }

            Builder profileFile(ProfileFile profileFile) {
                this.profileFile = profileFile;
                return this;
            }

            ProfileFileRefreshRecord build() {
                return new ProfileFileRefreshRecord(this);
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy