Skip to content

Commit b17bdec

Browse files
committed
Polishing.
Avoid nullability in RepositoryMethodContextHolder.getContext(). Introduce shortcut in RepositoryMethodContext to obtain the current thread-local context. Update documentation. See #3175. Original pull request: #3176
1 parent 24c31bf commit b17bdec

File tree

8 files changed

+76
-147
lines changed

8 files changed

+76
-147
lines changed

src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ package com.acme.search;
271271
272272
import org.springframework.beans.factory.annotation.Autowired;
273273
import org.springframework.data.domain.Limit;
274-
import org.springframework.data.repository.core.support.RepositoryMethodContext;
274+
import org.springframework.data.repository.core.RepositoryMethodContext;
275275
276276
class DefaultSearchExtension<T> implements SearchExtension<T> {
277277
@@ -282,7 +282,7 @@ class DefaultSearchExtension<T> implements SearchExtension<T> {
282282
}
283283
284284
public List<T> search(String text, Limit limit) {
285-
return search(RepositoryMethodContext.currentMethod(), text, limit);
285+
return search(RepositoryMethodContext.getContext(), text, limit);
286286
}
287287
288288
List<T> search(RepositoryMethodContext metadata, String text, Limit limit) {
@@ -297,12 +297,12 @@ class DefaultSearchExtension<T> implements SearchExtension<T> {
297297
}
298298
----
299299

300-
In the example above `RepositoryMethodContext.currentMethod()` is used to retrieve metadata for the actual method invocation.
300+
In the example above `RepositoryMethodContext.getContext()` is used to retrieve metadata for the actual method invocation.
301301
`RepositoryMethodContext` exposes information attached to the repository such as the domain type.
302302
In this case we use the repository domain type to identify the name of the index to be searched.
303303

304304
Exposing invocation metadata is costly, hence it is disabled by default.
305-
To access `RepositoryMethodContext.currentMethod()` you need to advise the repository factory responsible for creating the actual repository to expose method metadata.
305+
To access `RepositoryMethodContext.getContext()` you need to advise the repository factory responsible for creating the actual repository to expose method metadata.
306306

307307
.Expose Repository Metadata
308308
[tabs]
@@ -319,7 +319,7 @@ package com.acme.search;
319319
import org.springframework.beans.factory.annotation.Autowired;
320320
import org.springframework.data.domain.Limit;
321321
import org.springframework.data.repository.core.support.RepositoryMetadataAccess;
322-
import org.springframework.data.repository.core.support.RepositoryMethodContext;
322+
import org.springframework.data.repository.core.RepositoryMethodContext;
323323
324324
class DefaultSearchExtension<T> implements SearchExtension<T>, RepositoryMetadataAccess {
325325

src/main/java/org/springframework/data/repository/core/RepositoryMethodContext.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,24 @@
3333
* @author Christoph Strobl
3434
* @author Mark Paluch
3535
* @author Oliver Drotbohm
36-
* @since 3.4.0
36+
* @since 3.4
3737
*/
3838
public interface RepositoryMethodContext {
3939

40+
/**
41+
* Try to return the current repository method metadata. This method is usable only if the calling method has been
42+
* invoked via a repository method, and the repository factory has been set to expose metadata. Otherwise, this method
43+
* will throw an IllegalStateException.
44+
*
45+
* @return the current repository method metadata (never returns {@code null})
46+
* @throws IllegalStateException if the repository method metadata cannot be found, because the method was invoked
47+
* outside a repository method invocation context, or because the repository has not been configured to
48+
* expose its metadata.
49+
*/
50+
static RepositoryMethodContext getContext() throws IllegalStateException {
51+
return RepositoryMethodContextHolder.getContext();
52+
}
53+
4054
/**
4155
* Returns the metadata for the repository.
4256
*

src/main/java/org/springframework/data/repository/core/RepositoryMethodContextHolder.java

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,74 +19,68 @@
1919
import org.springframework.lang.Nullable;
2020

2121
/**
22+
* Associates a given {@link RepositoryMethodContext} with the current execution thread.
23+
* <p>
24+
* This class provides a series of static methods that interact with a thread-local storage of
25+
* {@link RepositoryMethodContext}. The purpose of the class is to provide a convenient way to be used for an
26+
* application.
27+
*
2228
* @author Christoph Strobl
23-
* @since 3.4.0
29+
* @author Mark Paluch
30+
* @since 3.4
31+
* @see RepositoryMethodContext
2432
*/
2533
public class RepositoryMethodContextHolder {
2634

27-
private static ContextProvider contextSupplier;
28-
29-
static {
30-
contextSupplier = new ThreadLocalContextProvider();
31-
}
32-
35+
/**
36+
* ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
37+
* "exposeMetadata" property on the controlling repository factory configuration has been set to {@code true}.
38+
*/
39+
private static final ThreadLocal<RepositoryMethodContext> currentMethod = new NamedThreadLocal<>(
40+
"Current Repository Method");
41+
42+
/**
43+
* Make the given repository method metadata available via the {@link #getContext()} method.
44+
* <p>
45+
* Note that the caller should be careful to keep the old value as appropriate.
46+
*
47+
* @param context the metadata to expose (or {@code null} to reset it)
48+
* @return the old metadata, which may be {@code null} if none was bound
49+
* @see #getContext()
50+
*/
3351
@Nullable
3452
public static RepositoryMethodContext setContext(@Nullable RepositoryMethodContext context) {
35-
return contextSupplier.set(context);
36-
}
37-
38-
@Nullable
39-
public static RepositoryMethodContext current() {
40-
return contextSupplier.get();
41-
}
42-
43-
public static void clearContext() {
44-
contextSupplier.clear();
45-
}
46-
47-
interface ContextProvider {
48-
49-
@Nullable
50-
RepositoryMethodContext get();
5153

52-
@Nullable
53-
RepositoryMethodContext set(@Nullable RepositoryMethodContext context);
54-
55-
void clear();
56-
}
57-
58-
static class ThreadLocalContextProvider implements ContextProvider {
59-
60-
/**
61-
* ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
62-
* "exposeMetadata" property on the controlling repository factory configuration has been set to "true".
63-
*/
64-
private static final ThreadLocal<RepositoryMethodContext> currentMethod = new NamedThreadLocal<>(
65-
"Current Repository Method");
66-
67-
@Override
68-
@Nullable
69-
public RepositoryMethodContext get() {
70-
return currentMethod.get();
71-
}
72-
73-
public void clear() {
54+
RepositoryMethodContext old = currentMethod.get();
55+
if (context != null) {
56+
currentMethod.set(context);
57+
} else {
7458
currentMethod.remove();
7559
}
7660

77-
@Override
78-
@Nullable
79-
public RepositoryMethodContext set(@Nullable RepositoryMethodContext context) {
80-
81-
RepositoryMethodContext old = currentMethod.get();
82-
83-
if (context != null) {
84-
currentMethod.set(context);
85-
} else {
86-
currentMethod.remove();
87-
}
61+
return old;
62+
}
8863

89-
return old;
64+
/**
65+
* Try to return the current repository method metadata. This method is usable only if the calling method has been
66+
* invoked via a repository method, and the repository factory has been set to expose metadata. Otherwise, this method
67+
* will throw an IllegalStateException.
68+
*
69+
* @return the current repository method metadata (never returns {@code null})
70+
* @throws IllegalStateException if the repository method metadata cannot be found, because the method was invoked
71+
* outside a repository method invocation context, or because the repository has not been configured to
72+
* expose its metadata.
73+
*/
74+
public static RepositoryMethodContext getContext() {
75+
76+
RepositoryMethodContext metadata = currentMethod.get();
77+
78+
if (metadata == null) {
79+
throw new IllegalStateException(
80+
"Cannot find current repository method: Set 'exposeMetadata' property on RepositoryFactorySupport to 'true' to make it available, and "
81+
+ "ensure that RepositoryMethodContext.getContext() is invoked in the same thread as the repository invocation.");
9082
}
83+
84+
return metadata;
9185
}
9286
}

src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMethodContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* @author Christoph Strobl
2828
* @author Mark Paluch
2929
* @author Oliver Drotbohm
30-
* @since 3.4.0
30+
* @since 3.4
3131
*/
3232
public class DefaultRepositoryMethodContext implements RepositoryMethodContext {
3333

src/main/java/org/springframework/data/repository/core/support/DynamicLookupTargetSource.java

Lines changed: 0 additions & 79 deletions
This file was deleted.

src/main/java/org/springframework/data/repository/core/support/RepositoryFactoryBeanSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
121121
* Default is "false", in order to avoid unnecessary extra interception. This means that no guarantees are provided
122122
* that {@code RepositoryMethodContext} access will work consistently within any method of the advised object.
123123
*
124-
* @since 3.4.0
124+
* @since 3.4
125125
*/
126126
public void setExposeMetadata(boolean exposeMetadata) {
127127
this.exposeMetadata = exposeMetadata;

src/main/java/org/springframework/data/repository/core/support/RepositoryMetadataAccess.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
*
2828
* @author Mark Paluch
2929
* @since 3.4
30-
* @see RepositoryMethodContext
30+
* @see org.springframework.data.repository.core.RepositoryMethodContext
3131
*/
3232
public interface RepositoryMetadataAccess {
3333

src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ void capturesRepositoryMetadata() {
256256
record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocation) {}
257257

258258
when(factory.queryOne.execute(any(Object[].class)))
259-
.then(invocation -> new Metadata(RepositoryMethodContextHolder.current(),
259+
.then(invocation -> new Metadata(RepositoryMethodContextHolder.getContext(),
260260
ExposeInvocationInterceptor.currentInvocation()));
261261

262262
factory.setExposeMetadata(true);
@@ -279,7 +279,7 @@ record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocati
279279
}
280280

281281
when(factory.queryOne.execute(any(Object[].class)))
282-
.then(invocation -> new Metadata(RepositoryMethodContextHolder.current(),
282+
.then(invocation -> new Metadata(RepositoryMethodContextHolder.getContext(),
283283
ExposeInvocationInterceptor.currentInvocation()));
284284

285285
var repository = factory.getRepository(ObjectRepository.class, new RepositoryMetadataAccess() {});

0 commit comments

Comments
 (0)