Skip to content

Commit e3e29a8

Browse files
authored
Per load context objects (graphql-java#27)
* Added the ability to have call context on batch loader calls * Better test of null context provider * Added map batch loaders so that you can be more natural in getting less results than asked for * Added the BatchLoadingEnvironment as a way to hang future objects into the call context * Now with repeated factory methods of the new different loader interfaces * Split dataloader into a helper implementation for most of the GUTS of the code * Spilling misteaks and gradma changes in doco * Javadoc p fix ups * Readme correction * Opps - didnt want that * Added per load context objects and changed the context provider interface to be just an object
1 parent c8eeacc commit e3e29a8

8 files changed

+264
-56
lines changed

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,17 @@ That said, with key caching turn on (the default), it will still be more efficie
168168
### Calling the batch loader function with call context environment
169169

170170
Often there is a need to call the batch loader function with some sort of call context environment, such as the calling users security
171-
credentials or the database connection parameters. You can do this by implementing a
172-
`org.dataloader.BatchLoaderEnvironmentProvider` and using one of the `xxxWithContext` batch loading interfaces
173-
such as `org.dataloader.BatchLoaderWithContext`.
171+
credentials or the database connection parameters.
174172

175-
```java
176-
BatchLoaderEnvironment batchLoaderEnvironment = BatchLoaderEnvironment.newBatchLoaderEnvironment()
177-
.context(SecurityCtx.getCallingUserCtx()).build();
173+
You can do this by implementing a `org.dataloader.BatchLoaderContextProvider` and using one of
174+
the batch loading interfaces such as `org.dataloader.BatchLoaderWithContext`.
175+
176+
It will be given a `org.dataloader.BatchLoaderEnvironment` parameter and it can then ask it
177+
for the context object.
178178

179+
```java
179180
DataLoaderOptions options = DataLoaderOptions.newOptions()
180-
.setBatchLoaderEnvironmentProvider(() -> batchLoaderEnvironment);
181+
.setBatchLoaderEnvironmentProvider(() -> SecurityCtx.getCallingUserCtx());
181182

182183
BatchLoaderWithContext<String, String> batchLoader = new BatchLoaderWithContext<String, String>() {
183184
@Override
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.dataloader;
2+
3+
/**
4+
* A BatchLoaderContextProvider is used by the {@link org.dataloader.DataLoader} code to
5+
* provide overall calling context to the {@link org.dataloader.BatchLoader} call. A common use
6+
* case is for propagating user security credentials or database connection parameters for example.
7+
*/
8+
public interface BatchLoaderContextProvider {
9+
/**
10+
* @return a context object that may be needed in batch load calls
11+
*/
12+
Object getContext();
13+
}

src/main/java/org/dataloader/BatchLoaderEnvironment.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,54 @@
11
package org.dataloader;
22

3+
import java.util.Collections;
4+
import java.util.Map;
5+
6+
import static org.dataloader.impl.Assertions.nonNull;
7+
38
/**
49
* This object is passed to a batch loader as calling context. It could contain security credentials
510
* of the calling users for example or database parameters that allow the data layer call to succeed.
611
*/
712
public class BatchLoaderEnvironment {
813

914
private final Object context;
15+
private final Map<Object, Object> keyContexts;
1016

11-
private BatchLoaderEnvironment(Object context) {
17+
private BatchLoaderEnvironment(Object context, Map<Object, Object> keyContexts) {
1218
this.context = context;
19+
this.keyContexts = keyContexts;
1320
}
1421

22+
/**
23+
* Returns the overall context object provided by {@link org.dataloader.BatchLoaderContextProvider}
24+
*
25+
* @param <T> the type you would like the object to be
26+
*
27+
* @return a context object or null if there isn't one
28+
*/
1529
@SuppressWarnings("unchecked")
1630
public <T> T getContext() {
1731
return (T) context;
1832
}
1933

34+
/**
35+
* Each call to {@link org.dataloader.DataLoader#load(Object, Object)} or
36+
* {@link org.dataloader.DataLoader#loadMany(java.util.List, java.util.List)} can be given
37+
* a context object when it is invoked. A map of them is present by this method.
38+
*
39+
* @return a map of key context objects
40+
*/
41+
public Map<Object, Object> getKeyContexts() {
42+
return keyContexts;
43+
}
44+
2045
public static Builder newBatchLoaderEnvironment() {
2146
return new Builder();
2247
}
2348

2449
public static class Builder {
2550
private Object context;
51+
private Map<Object, Object> keyContexts = Collections.emptyMap();
2652

2753
private Builder() {
2854

@@ -33,8 +59,13 @@ public Builder context(Object context) {
3359
return this;
3460
}
3561

62+
public Builder keyContexts(Map<Object, Object> keyContexts) {
63+
this.keyContexts = nonNull(keyContexts);
64+
return this;
65+
}
66+
3667
public BatchLoaderEnvironment build() {
37-
return new BatchLoaderEnvironment(context);
68+
return new BatchLoaderEnvironment(context, keyContexts);
3869
}
3970
}
4071
}

src/main/java/org/dataloader/DataLoader.java

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@
2020
import org.dataloader.stats.Statistics;
2121
import org.dataloader.stats.StatisticsCollector;
2222

23-
import java.util.AbstractMap.SimpleImmutableEntry;
2423
import java.util.ArrayList;
24+
import java.util.Collections;
2525
import java.util.List;
2626
import java.util.concurrent.CompletableFuture;
27-
import java.util.stream.Collectors;
2827

2928
import static org.dataloader.impl.Assertions.nonNull;
3029

@@ -60,7 +59,6 @@ public class DataLoader<K, V> {
6059
private final DataLoaderHelper<K, V> helper;
6160
private final DataLoaderOptions loaderOptions;
6261
private final CacheMap<Object, CompletableFuture<V>> futureCache;
63-
private final List<SimpleImmutableEntry<K, CompletableFuture<V>>> loaderQueue;
6462
private final StatisticsCollector stats;
6563

6664
/**
@@ -237,6 +235,7 @@ public static <K, V> DataLoader<K, V> newMappedDataLoader(MappedBatchLoader<K, V
237235
* Using Try objects allows you to capture a value returned or an exception that might
238236
* have occurred trying to get a value. .
239237
* <p>
238+
*
240239
* @param batchLoadFunction the batch load function to use that uses {@link org.dataloader.Try} objects
241240
* @param <K> the key type
242241
* @param <V> the value type
@@ -357,10 +356,9 @@ private DataLoader(Object batchLoadFunction, DataLoaderOptions options) {
357356
this.loaderOptions = options == null ? new DataLoaderOptions() : options;
358357
this.futureCache = determineCacheMap(loaderOptions);
359358
// order of keys matter in data loader
360-
this.loaderQueue = new ArrayList<>();
361359
this.stats = nonNull(this.loaderOptions.getStatisticsCollector());
362360

363-
this.helper = new DataLoaderHelper<>(this, batchLoadFunction, this.loaderOptions, this.futureCache, this.loaderQueue, this.stats);
361+
this.helper = new DataLoaderHelper<>(this, batchLoadFunction, this.loaderOptions, this.futureCache, this.stats);
364362
}
365363

366364
@SuppressWarnings("unchecked")
@@ -380,7 +378,26 @@ private CacheMap<Object, CompletableFuture<V>> determineCacheMap(DataLoaderOptio
380378
* @return the future of the value
381379
*/
382380
public CompletableFuture<V> load(K key) {
383-
return helper.load(key);
381+
return load(key, null);
382+
}
383+
384+
/**
385+
* Requests to load the data with the specified key asynchronously, and returns a future of the resulting value.
386+
* <p>
387+
* If batching is enabled (the default), you'll have to call {@link DataLoader#dispatch()} at a later stage to
388+
* start batch execution. If you forget this call the future will never be completed (unless already completed,
389+
* and returned from cache).
390+
* <p>
391+
* The key context object may be useful in the batch loader interfaces such as {@link org.dataloader.BatchLoaderWithContext} or
392+
* {@link org.dataloader.MappedBatchLoaderWithContext} to help retrieve data.
393+
*
394+
* @param key the key to load
395+
* @param keyContext a context object that is specific to this key
396+
*
397+
* @return the future of the value
398+
*/
399+
public CompletableFuture<V> load(K key, Object keyContext) {
400+
return helper.load(key, keyContext);
384401
}
385402

386403
/**
@@ -396,11 +413,39 @@ public CompletableFuture<V> load(K key) {
396413
* @return the composite future of the list of values
397414
*/
398415
public CompletableFuture<List<V>> loadMany(List<K> keys) {
399-
synchronized (this) {
400-
List<CompletableFuture<V>> collect = keys.stream()
401-
.map(this::load)
402-
.collect(Collectors.toList());
416+
return loadMany(keys, Collections.emptyList());
417+
}
403418

419+
/**
420+
* Requests to load the list of data provided by the specified keys asynchronously, and returns a composite future
421+
* of the resulting values.
422+
* <p>
423+
* If batching is enabled (the default), you'll have to call {@link DataLoader#dispatch()} at a later stage to
424+
* start batch execution. If you forget this call the future will never be completed (unless already completed,
425+
* and returned from cache).
426+
* <p>
427+
* The key context object may be useful in the batch loader interfaces such as {@link org.dataloader.BatchLoaderWithContext} or
428+
* {@link org.dataloader.MappedBatchLoaderWithContext} to help retrieve data.
429+
*
430+
* @param keys the list of keys to load
431+
* @param keyContexts the list of key calling context objects
432+
*
433+
* @return the composite future of the list of values
434+
*/
435+
public CompletableFuture<List<V>> loadMany(List<K> keys, List<Object> keyContexts) {
436+
nonNull(keys);
437+
nonNull(keyContexts);
438+
439+
synchronized (this) {
440+
List<CompletableFuture<V>> collect = new ArrayList<>();
441+
for (int i = 0; i < keys.size(); i++) {
442+
K key = keys.get(i);
443+
Object keyContext = null;
444+
if (i < keyContexts.size()) {
445+
keyContext = keyContexts.get(i);
446+
}
447+
collect.add(load(key, keyContext));
448+
}
404449
return CompletableFutureKit.allOf(collect);
405450
}
406451
}
@@ -441,9 +486,7 @@ public List<V> dispatchAndJoin() {
441486
* @return the depth of the batched key loads that need to be dispatched
442487
*/
443488
public int dispatchDepth() {
444-
synchronized (this) {
445-
return loaderQueue.size();
446-
}
489+
return helper.dispatchDepth();
447490
}
448491

449492

0 commit comments

Comments
 (0)