org.zalando.baigan.service.S3ConfigurationRepository Maven / Gradle / Ivy
package org.zalando.baigan.service;
import com.amazonaws.AmazonClientException;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClient;
import com.amazonaws.services.kms.model.DecryptRequest;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zalando.baigan.model.Configuration;
import org.zalando.baigan.service.aws.S3FileLoader;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class S3ConfigurationRepository extends AbstractConfigurationRepository {
private static final Logger LOG = LoggerFactory.getLogger(S3ConfigurationRepository.class);
/**
* The default refresh interval which is 60 seconds
*/
private static final long DEFAULT_REFRESH_INTERVAL = 60;
private final S3FileLoader s3Loader;
private long refreshInterval = DEFAULT_REFRESH_INTERVAL;
private volatile Map configurationsMap = ImmutableMap.of();
/**
* Provides a {@link ConfigurationRepository} that reads configurations from a JSON file stored in a S3 bucket.
* It refreshes configurations using the default refresh interval (60 seconds)
*
* @param bucketName The name of the bucket
* @param key The object key, usually, the "full path" to the JSON file stored in the bucket
*/
public S3ConfigurationRepository(@Nonnull final String bucketName, @Nonnull final String key) {
this(bucketName, key, DEFAULT_REFRESH_INTERVAL);
}
/**
* Provides a {@link ConfigurationRepository} that reads configurations from a JSON file stored in a S3 bucket.
*
* @param bucketName The name of the bucket
* @param key The object key, usually, the "full path" to the JSON file stored in the bucket
* @param refreshInterval The interval, in seconds, to refresh the configurations. A value of 0 disables refreshing
*
* @see #S3ConfigurationRepository(String, String)
*/
public S3ConfigurationRepository(@Nonnull final String bucketName, @Nonnull final String key, final long refreshInterval) {
checkNotNull(bucketName, "bucketName is required");
checkNotNull(key, "key is required");
checkArgument(refreshInterval >= 0, "refreshInterval has to be >= 0");
this.refreshInterval = refreshInterval;
s3Loader = new S3FileLoader(bucketName, key);
loadConfigurations();
if (refreshInterval > 0) {
setupRefresh();
}
}
protected void loadConfigurations() {
final String configurationText = s3Loader.loadContent();
final List configurations = getConfigurations(configurationText);
final ImmutableMap.Builder builder = ImmutableMap.builder();
for (final Configuration configuration : configurations) {
builder.put(configuration.getAlias(), configuration);
}
configurationsMap = builder.build();
}
private void setupRefresh() {
final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.scheduleAtFixedRate(() -> {
try {
loadConfigurations();
} catch (RuntimeException e) {
LOG.warn("Failed to refresh S3 configuration", e);
}
}, this.refreshInterval, this.refreshInterval,
TimeUnit.SECONDS);
}
@Nonnull
@Override
public Optional get(@Nonnull String key) {
return Optional.ofNullable(configurationsMap.get(key));
}
@Override
public void put(@Nonnull String key, @Nonnull String value) {
throw new UnsupportedOperationException("The S3ConfigurationRepository doesn't allow any changes.");
}
}