|  | 
| 19 | 19 | import org.springframework.lang.Nullable; | 
| 20 | 20 | 
 | 
| 21 | 21 | /** | 
|  | 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 | + * | 
| 22 | 28 |  * @author Christoph Strobl | 
| 23 |  | - * @since 3.4.0 | 
|  | 29 | + * @author Mark Paluch | 
|  | 30 | + * @since 3.4 | 
|  | 31 | + * @see RepositoryMethodContext | 
| 24 | 32 |  */ | 
| 25 | 33 | public class RepositoryMethodContextHolder { | 
| 26 | 34 | 
 | 
| 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 | +	 */ | 
| 33 | 51 | 	@Nullable | 
| 34 | 52 | 	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(); | 
| 51 | 53 | 
 | 
| 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 { | 
| 74 | 58 | 			currentMethod.remove(); | 
| 75 | 59 | 		} | 
| 76 | 60 | 
 | 
| 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 | +	} | 
| 88 | 63 | 
 | 
| 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.currentMethod() is invoked in the same thread as the repository invocation."); | 
| 90 | 82 | 		} | 
|  | 83 | + | 
|  | 84 | +		return metadata; | 
| 91 | 85 | 	} | 
| 92 | 86 | } | 
0 commit comments