1212 */
1313package com .amazonaws .services .dynamodbv2 .datamodeling .encryption .providers ;
1414
15- import com .amazonaws .services .dynamodbv2 .datamodeling .internal .LRUCache ;
15+ import java .util .concurrent .atomic .AtomicReference ;
16+ import java .util .concurrent .locks .ReentrantLock ;
17+
1618import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .EncryptionContext ;
1719import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .materials .DecryptionMaterials ;
1820import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .materials .EncryptionMaterials ;
1921import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .providers .store .ProviderStore ;
22+ import com .amazonaws .services .dynamodbv2 .datamodeling .internal .LRUCache ;
2023
2124/**
2225 * This meta-Provider encrypts data with the most recent version of keying materials from a
2528 * is not currently configurable.
2629 */
2730public class MostRecentProvider implements EncryptionMaterialsProvider {
28- private final Object lock ;
31+ private static final long MILLI_TO_NANO = 1000000L ;
32+ private static final long TTL_GRACE_IN_NANO = 500 * MILLI_TO_NANO ;
33+ private final ReentrantLock lock = new ReentrantLock (true );
2934 private final ProviderStore keystore ;
3035 private final String materialName ;
31- private final long ttlInMillis ;
36+ private final long ttlInNanos ;
3237 private final LRUCache <EncryptionMaterialsProvider > cache ;
33- private EncryptionMaterialsProvider currentProvider ;
34- private long currentVersion ;
35- private long lastUpdated ;
38+ private final AtomicReference <State > state = new AtomicReference <>(new State ());
3639
3740 /**
3841 * Creates a new {@link MostRecentProvider}.
@@ -43,30 +46,45 @@ public class MostRecentProvider implements EncryptionMaterialsProvider {
4346 public MostRecentProvider (final ProviderStore keystore , final String materialName , final long ttlInMillis ) {
4447 this .keystore = checkNotNull (keystore , "keystore must not be null" );
4548 this .materialName = checkNotNull (materialName , "materialName must not be null" );
46- this .ttlInMillis = ttlInMillis ;
49+ this .ttlInNanos = ttlInMillis * MILLI_TO_NANO ;
4750 this .cache = new LRUCache <EncryptionMaterialsProvider >(1000 );
48- this .lock = new Object ();
49- currentProvider = null ;
50- currentVersion = -1 ;
51- lastUpdated = 0 ;
5251 }
5352
5453 @ Override
5554 public EncryptionMaterials getEncryptionMaterials (EncryptionContext context ) {
56- synchronized (lock ) {
57- if ((System .currentTimeMillis () - lastUpdated ) > ttlInMillis ) {
58- long newVersion = keystore .getMaxVersion (materialName );
59- if (newVersion < 0 ) {
60- currentVersion = 0 ;
61- currentProvider = keystore .getOrCreate (materialName , currentVersion );
62- } else if (newVersion != currentVersion ) {
63- currentVersion = newVersion ;
64- currentProvider = keystore .getProvider (materialName , currentVersion );
65- cache .add (Long .toString (currentVersion ), currentProvider );
66- }
67- lastUpdated = System .currentTimeMillis ();
55+ State s = state .get ();
56+ if (System .nanoTime () - s .lastUpdated <= ttlInNanos ) {
57+ return s .provider .getEncryptionMaterials (context );
58+ }
59+ if (s .provider == null || System .nanoTime () - s .lastUpdated > ttlInNanos + TTL_GRACE_IN_NANO ) {
60+ // Either we don't have a provider at all, or we're more than 500 milliseconds past
61+ // our update time. Either way, grab the lock and force an update.
62+ lock .lock ();
63+ } else if (!lock .tryLock ()) {
64+ // If we can't get the lock immediately, just use the current provider
65+ return s .provider .getEncryptionMaterials (context );
66+ }
67+
68+ try {
69+ final long newVersion = keystore .getMaxVersion (materialName );
70+ final long currentVersion ;
71+ final EncryptionMaterialsProvider currentProvider ;
72+ if (newVersion < 0 ) {
73+ currentVersion = 0 ;
74+ currentProvider = keystore .getOrCreate (materialName , currentVersion );
75+ s = new State (currentProvider , currentVersion );
76+ state .set (s );
77+ } else if (newVersion != s .currentVersion ) {
78+ currentVersion = newVersion ;
79+ currentProvider = keystore .getProvider (materialName , currentVersion );
80+ cache .add (Long .toString (currentVersion ), currentProvider );
81+ s = new State (currentProvider , currentVersion );
82+ state .set (s );
6883 }
69- return currentProvider .getEncryptionMaterials (context );
84+
85+ return s .provider .getEncryptionMaterials (context );
86+ } finally {
87+ lock .unlock ();
7088 }
7189 }
7290
@@ -86,11 +104,7 @@ public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) {
86104 */
87105 @ Override
88106 public void refresh () {
89- synchronized (lock ) {
90- lastUpdated = 0 ;
91- currentVersion = -1 ;
92- currentProvider = null ;
93- }
107+ state .set (new State ());
94108 cache .clear ();
95109 }
96110
@@ -99,27 +113,23 @@ public String getMaterialName() {
99113 }
100114
101115 public long getTtlInMills () {
102- return ttlInMillis ;
116+ return ttlInNanos / MILLI_TO_NANO ;
103117 }
104118
105119 /**
106120 * The current version of the materials being used for encryption. Returns -1 if we do not
107121 * currently have a current version.
108122 */
109123 public long getCurrentVersion () {
110- synchronized (lock ) {
111- return currentVersion ;
112- }
124+ return state .get ().currentVersion ;
113125 }
114126
115127 /**
116128 * The last time the current version was updated. Returns 0 if we do not currently have a
117129 * current version.
118130 */
119131 public long getLastUpdated () {
120- synchronized (lock ) {
121- return lastUpdated ;
122- }
132+ return state .get ().lastUpdated / MILLI_TO_NANO ;
123133 }
124134
125135 private static <V > V checkNotNull (final V ref , final String errMsg ) {
@@ -129,4 +139,20 @@ private static <V> V checkNotNull(final V ref, final String errMsg) {
129139 return ref ;
130140 }
131141 }
142+
143+ private static class State {
144+ public final EncryptionMaterialsProvider provider ;
145+ public final long currentVersion ;
146+ public final long lastUpdated ;
147+
148+ public State () {
149+ this (null , -1 );
150+ }
151+
152+ public State (EncryptionMaterialsProvider provider , long currentVersion ) {
153+ this .provider = provider ;
154+ this .currentVersion = currentVersion ;
155+ this .lastUpdated = currentVersion == -1 ? 0 : System .nanoTime ();
156+ }
157+ }
132158}
0 commit comments