// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.base;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.twitter.common.quantity.Amount;
import com.twitter.common.quantity.Time;
import com.twitter.common.util.Clock;
/**
* A supplier that caches responses from an underling supplier, expiring the cached value after
* a fixed expiration time.
*
* @param <T> Supplied value type.
*
* @author William Farner
*/
public class CachingSupplier<T> implements Supplier<T> {
private final Supplier<T> wrapped;
private final long expirationNanos;
private final Clock clock;
private long lastFetchNanos = -1;
private T cachedValue;
/**
* Creates a new caching supplier.
*
* @param wrapped The supplier to delegate fetches to.
* @param expiration The maximum amount of time that a response from {@code supplier} will be
* cached for. The expiration must be positive.
*/
public CachingSupplier(Supplier<T> wrapped, Amount<Long, Time> expiration) {
this(wrapped, expiration, Clock.SYSTEM_CLOCK);
}
@VisibleForTesting
CachingSupplier(Supplier<T> wrapped, Amount<Long, Time> expiration, Clock clock) {
this.wrapped = Preconditions.checkNotNull(wrapped);
this.expirationNanos = Preconditions.checkNotNull(expiration).as(Time.NANOSECONDS);
Preconditions.checkArgument(expiration.getValue() > 0, "Expiration must be positive.");
this.clock = Preconditions.checkNotNull(clock);
}
@Override
public synchronized T get() {
if ((lastFetchNanos == -1) || (clock.nowNanos() - lastFetchNanos > expirationNanos)) {
cachedValue = wrapped.get();
lastFetchNanos = clock.nowNanos();
}
return cachedValue;
}
}