12
12
*/
13
13
package com .amazonaws .services .dynamodbv2 .datamodeling .encryption .providers ;
14
14
15
- import com .amazonaws .services .dynamodbv2 .datamodeling .internal .LRUCache ;
15
+ import java .util .concurrent .atomic .AtomicReference ;
16
+ import java .util .concurrent .locks .ReentrantLock ;
17
+
16
18
import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .EncryptionContext ;
17
19
import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .materials .DecryptionMaterials ;
18
20
import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .materials .EncryptionMaterials ;
19
21
import com .amazonaws .services .dynamodbv2 .datamodeling .encryption .providers .store .ProviderStore ;
22
+ import com .amazonaws .services .dynamodbv2 .datamodeling .internal .LRUCache ;
20
23
21
24
/**
22
25
* This meta-Provider encrypts data with the most recent version of keying materials from a
25
28
* is not currently configurable.
26
29
*/
27
30
public 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 );
29
34
private final ProviderStore keystore ;
30
35
private final String materialName ;
31
- private final long ttlInMillis ;
36
+ private final long ttlInNanos ;
32
37
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 ());
36
39
37
40
/**
38
41
* Creates a new {@link MostRecentProvider}.
@@ -43,30 +46,45 @@ public class MostRecentProvider implements EncryptionMaterialsProvider {
43
46
public MostRecentProvider (final ProviderStore keystore , final String materialName , final long ttlInMillis ) {
44
47
this .keystore = checkNotNull (keystore , "keystore must not be null" );
45
48
this .materialName = checkNotNull (materialName , "materialName must not be null" );
46
- this .ttlInMillis = ttlInMillis ;
49
+ this .ttlInNanos = ttlInMillis * MILLI_TO_NANO ;
47
50
this .cache = new LRUCache <EncryptionMaterialsProvider >(1000 );
48
- this .lock = new Object ();
49
- currentProvider = null ;
50
- currentVersion = -1 ;
51
- lastUpdated = 0 ;
52
51
}
53
52
54
53
@ Override
55
54
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 );
68
83
}
69
- return currentProvider .getEncryptionMaterials (context );
84
+
85
+ return s .provider .getEncryptionMaterials (context );
86
+ } finally {
87
+ lock .unlock ();
70
88
}
71
89
}
72
90
@@ -86,11 +104,7 @@ public DecryptionMaterials getDecryptionMaterials(EncryptionContext context) {
86
104
*/
87
105
@ Override
88
106
public void refresh () {
89
- synchronized (lock ) {
90
- lastUpdated = 0 ;
91
- currentVersion = -1 ;
92
- currentProvider = null ;
93
- }
107
+ state .set (new State ());
94
108
cache .clear ();
95
109
}
96
110
@@ -99,27 +113,23 @@ public String getMaterialName() {
99
113
}
100
114
101
115
public long getTtlInMills () {
102
- return ttlInMillis ;
116
+ return ttlInNanos / MILLI_TO_NANO ;
103
117
}
104
118
105
119
/**
106
120
* The current version of the materials being used for encryption. Returns -1 if we do not
107
121
* currently have a current version.
108
122
*/
109
123
public long getCurrentVersion () {
110
- synchronized (lock ) {
111
- return currentVersion ;
112
- }
124
+ return state .get ().currentVersion ;
113
125
}
114
126
115
127
/**
116
128
* The last time the current version was updated. Returns 0 if we do not currently have a
117
129
* current version.
118
130
*/
119
131
public long getLastUpdated () {
120
- synchronized (lock ) {
121
- return lastUpdated ;
122
- }
132
+ return state .get ().lastUpdated / MILLI_TO_NANO ;
123
133
}
124
134
125
135
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) {
129
139
return ref ;
130
140
}
131
141
}
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
+ }
132
158
}
0 commit comments