55import org .dataloader .instrumentation .DataLoaderInstrumentation ;
66import org .dataloader .instrumentation .DataLoaderInstrumentationHelper ;
77import org .dataloader .stats .Statistics ;
8+ import org .jspecify .annotations .NullMarked ;
9+ import org .jspecify .annotations .Nullable ;
810
911import java .util .ArrayList ;
1012import java .util .HashMap ;
1618import java .util .concurrent .ConcurrentHashMap ;
1719import java .util .function .Function ;
1820
21+ import static org .dataloader .impl .Assertions .assertState ;
22+
1923/**
2024 * This allows data loaders to be registered together into a single place, so
2125 * they can be dispatched as one. It also allows you to retrieve data loaders by
3539 * are the same object, then nothing is changed, since the same instrumentation code is being run.
3640 */
3741@ PublicApi
42+ @ NullMarked
3843public class DataLoaderRegistry {
3944 protected final Map <String , DataLoader <?, ?>> dataLoaders ;
40- protected final DataLoaderInstrumentation instrumentation ;
45+ protected final @ Nullable DataLoaderInstrumentation instrumentation ;
4146
4247
4348 public DataLoaderRegistry () {
@@ -48,27 +53,30 @@ private DataLoaderRegistry(Builder builder) {
4853 this (builder .dataLoaders , builder .instrumentation );
4954 }
5055
51- protected DataLoaderRegistry (Map <String , DataLoader <?, ?>> dataLoaders , DataLoaderInstrumentation instrumentation ) {
56+ protected DataLoaderRegistry (Map <String , DataLoader <?, ?>> dataLoaders , @ Nullable DataLoaderInstrumentation instrumentation ) {
5257 this .dataLoaders = instrumentDLs (dataLoaders , instrumentation );
5358 this .instrumentation = instrumentation ;
5459 }
5560
56- private Map <String , DataLoader <?, ?>> instrumentDLs (Map <String , DataLoader <?, ?>> incomingDataLoaders , DataLoaderInstrumentation registryInstrumentation ) {
61+ private Map <String , DataLoader <?, ?>> instrumentDLs (Map <String , DataLoader <?, ?>> incomingDataLoaders , @ Nullable DataLoaderInstrumentation registryInstrumentation ) {
5762 Map <String , DataLoader <?, ?>> dataLoaders = new ConcurrentHashMap <>(incomingDataLoaders );
5863 if (registryInstrumentation != null ) {
59- dataLoaders .replaceAll ((k , existingDL ) -> instrumentDL ( registryInstrumentation , existingDL ));
64+ dataLoaders .replaceAll ((k , existingDL ) -> nameAndInstrumentDL ( k , registryInstrumentation , existingDL ));
6065 }
6166 return dataLoaders ;
6267 }
6368
6469 /**
6570 * Can be called to tweak a {@link DataLoader} so that it has the registry {@link DataLoaderInstrumentation} added as the first one.
6671 *
72+ * @param key the key used to register the data loader
6773 * @param registryInstrumentation the common registry {@link DataLoaderInstrumentation}
6874 * @param existingDL the existing data loader
6975 * @return a new {@link DataLoader} or the same one if there is nothing to change
7076 */
71- private static DataLoader <?, ?> instrumentDL (DataLoaderInstrumentation registryInstrumentation , DataLoader <?, ?> existingDL ) {
77+ private static DataLoader <?, ?> nameAndInstrumentDL (String key , @ Nullable DataLoaderInstrumentation registryInstrumentation , DataLoader <?, ?> existingDL ) {
78+ existingDL = checkAndSetName (key , existingDL );
79+
7280 if (registryInstrumentation == null ) {
7381 return existingDL ;
7482 }
@@ -97,6 +105,15 @@ protected DataLoaderRegistry(Map<String, DataLoader<?, ?>> dataLoaders, DataLoad
97105 }
98106 }
99107
108+ private static DataLoader <?, ?> checkAndSetName (String key , DataLoader <?, ?> dataLoader ) {
109+ if (dataLoader .getName () == null ) {
110+ return dataLoader .transform (b -> b .name (key ));
111+ }
112+ assertState (key .equals (dataLoader .getName ()),
113+ () -> String .format ("Data loader name '%s' is not the same as registered key '%s'" , dataLoader .getName (), key ));
114+ return dataLoader ;
115+ }
116+
100117 private static DataLoader <?, ?> mkInstrumentedDataLoader (DataLoader <?, ?> existingDL , DataLoaderOptions options , DataLoaderInstrumentation newInstrumentation ) {
101118 return existingDL .transform (builder -> builder .options (setInInstrumentation (options , newInstrumentation )));
102119 }
@@ -108,28 +125,70 @@ private static DataLoaderOptions setInInstrumentation(DataLoaderOptions options,
108125 /**
109126 * @return the {@link DataLoaderInstrumentation} associated with this registry which can be null
110127 */
111- public DataLoaderInstrumentation getInstrumentation () {
128+ public @ Nullable DataLoaderInstrumentation getInstrumentation () {
112129 return instrumentation ;
113130 }
114131
115132 /**
116- * This will register a new dataloader
133+ * This will register a new named dataloader. The {@link DataLoader} must be named something and
134+ * cannot have a null name.
135+ * <p>
136+ * Note: Registration can change the data loader instance since it might get an {@link DataLoaderInstrumentation} applied to
137+ * it. So the {@link DataLoader} instance your read via {@link DataLoaderRegistry#getDataLoader(String)} might not be the same
138+ * object that was registered.
139+ *
140+ * @param dataLoader the named data loader to register
141+ * @return this registry
142+ */
143+ public DataLoaderRegistry register (DataLoader <?, ?> dataLoader ) {
144+ String name = dataLoader .getName ();
145+ assertState (name != null , () -> "The DataLoader must have a non null name" );
146+ dataLoaders .put (name , nameAndInstrumentDL (name , instrumentation , dataLoader ));
147+ return this ;
148+ }
149+
150+ /**
151+ * This will register a new {@link DataLoader}
152+ * <p>
153+ * Note: Registration can change the data loader instance since it might get an {@link DataLoaderInstrumentation} applied to
154+ * it. So the {@link DataLoader} instance your read via {@link DataLoaderRegistry#getDataLoader(String)} might not be the same
155+ * object that was registered.
117156 *
118157 * @param key the key to put the data loader under
119158 * @param dataLoader the data loader to register
120159 * @return this registry
121160 */
122161 public DataLoaderRegistry register (String key , DataLoader <?, ?> dataLoader ) {
123- dataLoaders .put (key , instrumentDL ( instrumentation , dataLoader ));
162+ dataLoaders .put (key , nameAndInstrumentDL ( key , instrumentation , dataLoader ));
124163 return this ;
125164 }
126165
166+ /**
167+ * This will register a new {@link DataLoader} and then return it.
168+ * <p>
169+ * Note: Registration can change the data loader instance since it might get an {@link DataLoaderInstrumentation} applied to
170+ * it. So the {@link DataLoader} instance your read via {@link DataLoaderRegistry#getDataLoader(String)} might not be the same
171+ * object that was registered.
172+ *
173+ * @param key the key to put the data loader under
174+ * @param dataLoader the data loader to register
175+ * @return the data loader instance that was registered
176+ */
177+ public <K , V > DataLoader <K , V > registerAndGet (String key , DataLoader <?, ?> dataLoader ) {
178+ dataLoaders .put (key , nameAndInstrumentDL (key , instrumentation , dataLoader ));
179+ return getDataLoader (key );
180+ }
181+
127182 /**
128183 * Computes a data loader if absent or return it if it was
129184 * already registered at that key.
130185 * <p>
131186 * Note: The entire method invocation is performed atomically,
132187 * so the function is applied at most once per key.
188+ * <p>
189+ * Note: Registration can change the data loader instance since it might get an {@link DataLoaderInstrumentation} applied to
190+ * it. So the {@link DataLoader} instance your read via {@link DataLoaderRegistry#getDataLoader(String)} might not be the same
191+ * object that was registered.
133192 *
134193 * @param key the key of the data loader
135194 * @param mappingFunction the function to compute a data loader
@@ -142,7 +201,7 @@ public <K, V> DataLoader<K, V> computeIfAbsent(final String key,
142201 final Function <String , DataLoader <?, ?>> mappingFunction ) {
143202 return (DataLoader <K , V >) dataLoaders .computeIfAbsent (key , (k ) -> {
144203 DataLoader <?, ?> dl = mappingFunction .apply (k );
145- return instrumentDL ( instrumentation , dl );
204+ return nameAndInstrumentDL ( key , instrumentation , dl );
146205 });
147206 }
148207
@@ -262,7 +321,7 @@ public static Builder newRegistry() {
262321 public static class Builder {
263322
264323 private final Map <String , DataLoader <?, ?>> dataLoaders = new HashMap <>();
265- private DataLoaderInstrumentation instrumentation ;
324+ private @ Nullable DataLoaderInstrumentation instrumentation ;
266325
267326 /**
268327 * This will register a new dataloader
0 commit comments