5
5
import org .dataloader .annotations .ExperimentalApi ;
6
6
7
7
import java .time .Duration ;
8
- import java .util .HashMap ;
8
+ import java .util .LinkedHashMap ;
9
9
import java .util .Map ;
10
+ import java .util .concurrent .ConcurrentHashMap ;
10
11
import java .util .concurrent .Executors ;
11
12
import java .util .concurrent .ScheduledExecutorService ;
12
13
import java .util .concurrent .TimeUnit ;
13
14
14
15
import static org .dataloader .impl .Assertions .nonNull ;
15
16
16
17
/**
17
- * This {@link DataLoaderRegistry} will use a {@link DispatchPredicate} when {@link #dispatchAll()} is called
18
+ * This {@link DataLoaderRegistry} will use {@link DispatchPredicate}s when {@link #dispatchAll()} is called
18
19
* to test (for each {@link DataLoader} in the registry) if a dispatch should proceed. If the predicate returns false, then a task is scheduled
19
20
* to perform that predicate dispatch again via the {@link ScheduledExecutorService}.
20
21
* <p>
22
+ * It;s possible to have a {@link DispatchPredicate} per dataloader as well as a default {@link DispatchPredicate} for the
23
+ * whole {@link ScheduledDataLoaderRegistry}.
24
+ * <p>
21
25
* This will continue to loop (test false and reschedule) until such time as the predicate returns true, in which case
22
- * no rescheduling will occur and you will need to call dispatch again to restart the process.
26
+ * no rescheduling will occur, and you will need to call dispatch again to restart the process.
23
27
* <p>
24
28
* If you wanted to create a ScheduledDataLoaderRegistry that started a rescheduling immediately, just create one and
25
29
* call {@link #rescheduleNow()}.
29
33
@ ExperimentalApi
30
34
public class ScheduledDataLoaderRegistry extends DataLoaderRegistry implements AutoCloseable {
31
35
32
- private final ScheduledExecutorService scheduledExecutorService ;
36
+ private final Map < DataLoader <?, ?>, DispatchPredicate > dataLoaderPredicates = new ConcurrentHashMap <>() ;
33
37
private final DispatchPredicate dispatchPredicate ;
38
+ private final ScheduledExecutorService scheduledExecutorService ;
34
39
private final Duration schedule ;
35
40
private volatile boolean closed ;
36
41
37
42
private ScheduledDataLoaderRegistry (Builder builder ) {
43
+ super ();
38
44
this .dataLoaders .putAll (builder .dataLoaders );
39
45
this .scheduledExecutorService = builder .scheduledExecutorService ;
40
- this .dispatchPredicate = builder .dispatchPredicate ;
41
46
this .schedule = builder .schedule ;
42
47
this .closed = false ;
48
+ this .dispatchPredicate = builder .dispatchPredicate ;
49
+ this .dataLoaderPredicates .putAll (builder .dataLoaderPredicates );
43
50
}
44
51
45
52
/**
@@ -57,6 +64,88 @@ public Duration getScheduleDuration() {
57
64
return schedule ;
58
65
}
59
66
67
+ /**
68
+ * This will combine all the current data loaders in this registry and all the data loaders from the specified registry
69
+ * and return a new combined registry
70
+ *
71
+ * @param registry the registry to combine into this registry
72
+ *
73
+ * @return a new combined registry
74
+ */
75
+ public ScheduledDataLoaderRegistry combine (DataLoaderRegistry registry ) {
76
+ Builder combinedBuilder = ScheduledDataLoaderRegistry .newScheduledRegistry ()
77
+ .dispatchPredicate (this .dispatchPredicate );
78
+ combinedBuilder .registerAll (this );
79
+ combinedBuilder .registerAll (registry );
80
+ return combinedBuilder .build ();
81
+ }
82
+
83
+
84
+ /**
85
+ * This will unregister a new dataloader
86
+ *
87
+ * @param key the key of the data loader to unregister
88
+ *
89
+ * @return this registry
90
+ */
91
+ public ScheduledDataLoaderRegistry unregister (String key ) {
92
+ DataLoader <?, ?> dataLoader = dataLoaders .remove (key );
93
+ if (dataLoader != null ) {
94
+ dataLoaderPredicates .remove (dataLoader );
95
+ }
96
+ return this ;
97
+ }
98
+
99
+ /**
100
+ * @return a map of data loaders to specific dispatch predicates
101
+ */
102
+ public Map <DataLoader <?, ?>, DispatchPredicate > getDataLoaderPredicates () {
103
+ return new LinkedHashMap <>(dataLoaderPredicates );
104
+ }
105
+
106
+ /**
107
+ * There is a default predicate that applies to the whole {@link ScheduledDataLoaderRegistry}
108
+ *
109
+ * @return the default dispatch predicate
110
+ */
111
+ public DispatchPredicate getDispatchPredicate () {
112
+ return dispatchPredicate ;
113
+ }
114
+
115
+ /**
116
+ * This will register a new dataloader and dispatch predicate associated with that data loader
117
+ *
118
+ * @param key the key to put the data loader under
119
+ * @param dataLoader the data loader to register
120
+ * @param dispatchPredicate the dispatch predicate to associate with this data loader
121
+ *
122
+ * @return this registry
123
+ */
124
+ public ScheduledDataLoaderRegistry register (String key , DataLoader <?, ?> dataLoader , DispatchPredicate dispatchPredicate ) {
125
+ dataLoaders .put (key , dataLoader );
126
+ dataLoaderPredicates .put (dataLoader , dispatchPredicate );
127
+ return this ;
128
+ }
129
+
130
+ /**
131
+ * Returns true if the dataloader has a predicate which returned true, OR the overall
132
+ * registry predicate returned true.
133
+ *
134
+ * @param dataLoaderKey the key in the dataloader map
135
+ * @param dataLoader the dataloader
136
+ *
137
+ * @return true if it should dispatch
138
+ */
139
+ private boolean shouldDispatch (String dataLoaderKey , DataLoader <?, ?> dataLoader ) {
140
+ DispatchPredicate dispatchPredicate = dataLoaderPredicates .get (dataLoader );
141
+ if (dispatchPredicate != null ) {
142
+ if (dispatchPredicate .test (dataLoaderKey , dataLoader )) {
143
+ return true ;
144
+ }
145
+ }
146
+ return this .dispatchPredicate .test (dataLoaderKey , dataLoader );
147
+ }
148
+
60
149
@ Override
61
150
public void dispatchAll () {
62
151
dispatchAllWithCount ();
@@ -68,7 +157,7 @@ public int dispatchAllWithCount() {
68
157
for (Map .Entry <String , DataLoader <?, ?>> entry : dataLoaders .entrySet ()) {
69
158
DataLoader <?, ?> dataLoader = entry .getValue ();
70
159
String key = entry .getKey ();
71
- if (dispatchPredicate . test (key , dataLoader )) {
160
+ if (shouldDispatch (key , dataLoader )) {
72
161
sum += dataLoader .dispatchWithCounts ().getKeysCount ();
73
162
} else {
74
163
reschedule (key , dataLoader );
@@ -77,24 +166,28 @@ public int dispatchAllWithCount() {
77
166
return sum ;
78
167
}
79
168
169
+
80
170
/**
81
171
* This will immediately dispatch the {@link DataLoader}s in the registry
82
- * without testing the predicate
172
+ * without testing the predicates
83
173
*/
84
174
public void dispatchAllImmediately () {
85
- super . dispatchAll ();
175
+ dispatchAllWithCountImmediately ();
86
176
}
87
177
88
178
/**
89
179
* This will immediately dispatch the {@link DataLoader}s in the registry
90
- * without testing the predicate
180
+ * without testing the predicates
91
181
*
92
182
* @return total number of entries that were dispatched from registered {@link org.dataloader.DataLoader}s.
93
183
*/
94
184
public int dispatchAllWithCountImmediately () {
95
- return super .dispatchAllWithCount ();
185
+ return dataLoaders .values ().stream ()
186
+ .mapToInt (dataLoader -> dataLoader .dispatchWithCounts ().getKeysCount ())
187
+ .sum ();
96
188
}
97
189
190
+
98
191
/**
99
192
* This will schedule a task to check the predicate and dispatch if true right now. It will not do
100
193
* a pre check of the preodicate like {@link #dispatchAll()} would
@@ -111,16 +204,16 @@ private void reschedule(String key, DataLoader<?, ?> dataLoader) {
111
204
}
112
205
113
206
private void dispatchOrReschedule (String key , DataLoader <?, ?> dataLoader ) {
114
- if (dispatchPredicate . test (key , dataLoader )) {
207
+ if (shouldDispatch (key , dataLoader )) {
115
208
dataLoader .dispatch ();
116
209
} else {
117
210
reschedule (key , dataLoader );
118
211
}
119
212
}
120
213
121
214
/**
122
- * By default this will create use a {@link Executors#newSingleThreadScheduledExecutor()}
123
- * and a schedule duration of 10 milli seconds .
215
+ * By default, this will create use a {@link Executors#newSingleThreadScheduledExecutor()}
216
+ * and a schedule duration of 10 milliseconds .
124
217
*
125
218
* @return A builder of {@link ScheduledDataLoaderRegistry}s
126
219
*/
@@ -130,10 +223,11 @@ public static Builder newScheduledRegistry() {
130
223
131
224
public static class Builder {
132
225
226
+ private final Map <String , DataLoader <?, ?>> dataLoaders = new LinkedHashMap <>();
227
+ private final Map <DataLoader <?, ?>, DispatchPredicate > dataLoaderPredicates = new LinkedHashMap <>();
228
+ private DispatchPredicate dispatchPredicate = DispatchPredicate .DISPATCH_ALWAYS ;
133
229
private ScheduledExecutorService scheduledExecutorService = Executors .newSingleThreadScheduledExecutor ();
134
- private DispatchPredicate dispatchPredicate = (key , dl ) -> true ;
135
230
private Duration schedule = Duration .ofMillis (10 );
136
- private final Map <String , DataLoader <?, ?>> dataLoaders = new HashMap <>();
137
231
138
232
public Builder scheduledExecutorService (ScheduledExecutorService executorService ) {
139
233
this .scheduledExecutorService = nonNull (executorService );
@@ -145,11 +239,6 @@ public Builder schedule(Duration schedule) {
145
239
return this ;
146
240
}
147
241
148
- public Builder dispatchPredicate (DispatchPredicate dispatchPredicate ) {
149
- this .dispatchPredicate = nonNull (dispatchPredicate );
150
- return this ;
151
- }
152
-
153
242
/**
154
243
* This will register a new dataloader
155
244
*
@@ -163,8 +252,24 @@ public Builder register(String key, DataLoader<?, ?> dataLoader) {
163
252
return this ;
164
253
}
165
254
255
+
166
256
/**
167
- * This will combine together the data loaders in this builder with the ones
257
+ * This will register a new dataloader with a specific {@link DispatchPredicate}
258
+ *
259
+ * @param key the key to put the data loader under
260
+ * @param dataLoader the data loader to register
261
+ * @param dispatchPredicate the dispatch predicate
262
+ *
263
+ * @return this builder for a fluent pattern
264
+ */
265
+ public Builder register (String key , DataLoader <?, ?> dataLoader , DispatchPredicate dispatchPredicate ) {
266
+ register (key , dataLoader );
267
+ dataLoaderPredicates .put (dataLoader , dispatchPredicate );
268
+ return this ;
269
+ }
270
+
271
+ /**
272
+ * This will combine the data loaders in this builder with the ones
168
273
* from a previous {@link DataLoaderRegistry}
169
274
*
170
275
* @param otherRegistry the previous {@link DataLoaderRegistry}
@@ -173,6 +278,23 @@ public Builder register(String key, DataLoader<?, ?> dataLoader) {
173
278
*/
174
279
public Builder registerAll (DataLoaderRegistry otherRegistry ) {
175
280
dataLoaders .putAll (otherRegistry .getDataLoadersMap ());
281
+ if (otherRegistry instanceof ScheduledDataLoaderRegistry ) {
282
+ ScheduledDataLoaderRegistry other = (ScheduledDataLoaderRegistry ) otherRegistry ;
283
+ dataLoaderPredicates .putAll (other .dataLoaderPredicates );
284
+ }
285
+ return this ;
286
+ }
287
+
288
+ /**
289
+ * This sets a default predicate on the {@link DataLoaderRegistry} that will control
290
+ * whether all {@link DataLoader}s in the {@link DataLoaderRegistry }should be dispatched.
291
+ *
292
+ * @param dispatchPredicate the predicate
293
+ *
294
+ * @return this builder for a fluent pattern
295
+ */
296
+ public Builder dispatchPredicate (DispatchPredicate dispatchPredicate ) {
297
+ this .dispatchPredicate = dispatchPredicate ;
176
298
return this ;
177
299
}
178
300
0 commit comments