diff --git a/core/src/main/java/google/registry/model/AppEngineEnvironment.java b/core/src/main/java/google/registry/model/AppEngineEnvironment.java
index f71ad1238..80f2942d2 100644
--- a/core/src/main/java/google/registry/model/AppEngineEnvironment.java
+++ b/core/src/main/java/google/registry/model/AppEngineEnvironment.java
@@ -116,4 +116,9 @@ public class AppEngineEnvironment {
}
});
}
+
+ /** Returns true if the current thread is in an App Engine Environment. */
+ public static boolean isInAppEngineEnvironment() {
+ return ApiProxy.getCurrentEnvironment() != null;
+ }
}
diff --git a/core/src/main/java/google/registry/model/CacheUtils.java b/core/src/main/java/google/registry/model/CacheUtils.java
index 81d5d3615..37519c3ad 100644
--- a/core/src/main/java/google/registry/model/CacheUtils.java
+++ b/core/src/main/java/google/registry/model/CacheUtils.java
@@ -18,9 +18,13 @@ import static com.google.common.base.Suppliers.memoizeWithExpiration;
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.base.Supplier;
+import google.registry.model.annotations.DeleteAfterMigration;
import java.time.Duration;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** Utility methods related to caching Datastore entities. */
public class CacheUtils {
@@ -28,8 +32,8 @@ public class CacheUtils {
/**
* Memoize a supplier, with a short expiration specified in the environment config.
*
- *
Use this for things that might change while code is running. (For example, the various
- * lists downloaded from the TMCH get updated in Datastore and the caches need to be refreshed.)
+ *
Use this for things that might change while code is running. (For example, the various lists
+ * downloaded from the TMCH get updated in Datastore and the caches need to be refreshed.)
*/
public static Supplier memoizeWithShortExpiration(Supplier original) {
return tryMemoizeWithExpiration(getSingletonCacheRefreshDuration(), original);
@@ -73,4 +77,29 @@ public class CacheUtils {
}
return caffeine;
}
+
+ /**
+ * A {@link CacheLoader} that automatically masquerade the background thread where the refresh
+ * action runs in to be an GAE thread.
+ */
+ @DeleteAfterMigration
+ public abstract static class AppEngineEnvironmentCacheLoader implements CacheLoader {
+
+ private static final AppEngineEnvironment environment = new AppEngineEnvironment();
+
+ @Override
+ public @Nullable V reload(@NonNull K key, @NonNull V oldValue) throws Exception {
+ V value;
+ boolean isMasqueraded = false;
+ if (!AppEngineEnvironment.isInAppEngineEnvironment()) {
+ environment.setEnvironmentForCurrentThread();
+ isMasqueraded = true;
+ }
+ value = load(key);
+ if (isMasqueraded) {
+ environment.unsetEnvironmentForCurrentThread();
+ }
+ return value;
+ }
+ }
}
diff --git a/core/src/main/java/google/registry/model/EppResource.java b/core/src/main/java/google/registry/model/EppResource.java
index b1e37110b..b10248e50 100644
--- a/core/src/main/java/google/registry/model/EppResource.java
+++ b/core/src/main/java/google/registry/model/EppResource.java
@@ -37,6 +37,7 @@ import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import google.registry.config.RegistryConfig;
+import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
@@ -385,7 +386,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
}
static final CacheLoader, EppResource> CACHE_LOADER =
- new CacheLoader, EppResource>() {
+ new AppEngineEnvironmentCacheLoader, EppResource>() {
@Override
public EppResource load(VKey extends EppResource> key) {
diff --git a/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java b/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java
index 0e70f6c54..0b7e9ee3a 100644
--- a/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java
+++ b/core/src/main/java/google/registry/model/index/ForeignKeyIndex.java
@@ -43,6 +43,7 @@ import com.googlecode.objectify.annotation.Index;
import google.registry.config.RegistryConfig;
import google.registry.model.BackupGroupRoot;
import google.registry.model.CacheUtils;
+import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
import google.registry.model.EppResource;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.ReportedOn;
@@ -256,7 +257,8 @@ public abstract class ForeignKeyIndex extends BackupGroup
}
static final CacheLoader>, Optional>> CACHE_LOADER =
- new CacheLoader>, Optional>>() {
+ new AppEngineEnvironmentCacheLoader<
+ VKey>, Optional>>() {
@Override
public Optional> load(VKey> key) {
diff --git a/core/src/main/java/google/registry/model/tld/Registry.java b/core/src/main/java/google/registry/model/tld/Registry.java
index cbfab4875..2d05e3cd3 100644
--- a/core/src/main/java/google/registry/model/tld/Registry.java
+++ b/core/src/main/java/google/registry/model/tld/Registry.java
@@ -29,7 +29,6 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static org.joda.money.CurrencyUnit.USD;
-import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
@@ -50,6 +49,7 @@ import com.googlecode.objectify.annotation.OnSave;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.CacheUtils;
+import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
@@ -268,7 +268,7 @@ public class Registry extends ImmutableObject
private static final LoadingCache> CACHE =
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
.build(
- new CacheLoader>() {
+ new AppEngineEnvironmentCacheLoader>() {
@Override
public Optional load(final String tld) {
// Enter a transaction-less context briefly; we don't want to enroll every TLD in
diff --git a/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java b/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java
index e1c21b63b..6a4091336 100644
--- a/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java
+++ b/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java
@@ -19,12 +19,12 @@ import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PROD
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static google.registry.util.ResourceUtils.readResourceUtf8;
-import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
import google.registry.model.CacheUtils;
+import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
import google.registry.model.tmch.TmchCrl;
import google.registry.util.Clock;
import google.registry.util.X509Utils;
@@ -78,7 +78,7 @@ public final class TmchCertificateAuthority {
private static final LoadingCache CRL_CACHE =
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
.build(
- new CacheLoader() {
+ new AppEngineEnvironmentCacheLoader() {
@Override
public X509CRL load(final TmchCaMode tmchCaMode) throws GeneralSecurityException {
Optional storedCrl = TmchCrl.get();