Polishing
This commit is contained in:
		
							parent
							
								
									e59a5993f3
								
							
						
					
					
						commit
						382a931e7d
					
				| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.core;
 | 
			
		||||
 | 
			
		||||
import org.reactivestreams.Publisher;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +23,8 @@ import reactor.core.publisher.Mono;
 | 
			
		|||
/**
 | 
			
		||||
 * Contract for adapting to and from {@link Flux} and {@link Mono}.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>An adapter supports a specific adaptee type whose stream semantics can be
 | 
			
		||||
 * checked via {@link #getDescriptor()}.
 | 
			
		||||
 * <p>An adapter supports a specific adaptee type whose stream semantics
 | 
			
		||||
 * can be checked via {@link #getDescriptor()}.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>Use the {@link ReactiveAdapterRegistry} to obtain an adapter for a
 | 
			
		||||
 * supported adaptee type or to register additional adapters.
 | 
			
		||||
| 
						 | 
				
			
			@ -78,14 +79,12 @@ public interface ReactiveAdapter {
 | 
			
		|||
 | 
			
		||||
		private final boolean isNoValue;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		public Descriptor(boolean isMultiValue, boolean canBeEmpty, boolean isNoValue) {
 | 
			
		||||
			this.isMultiValue = isMultiValue;
 | 
			
		||||
			this.supportsEmpty = canBeEmpty;
 | 
			
		||||
			this.isNoValue = isNoValue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Return {@code true} if the adaptee implies 0..N values can be produced
 | 
			
		||||
		 * and is therefore a good fit to adapt to {@link Flux}. A {@code false}
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +109,6 @@ public interface ReactiveAdapter {
 | 
			
		|||
		public boolean isNoValue() {
 | 
			
		||||
			return this.isNoValue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.core;
 | 
			
		||||
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +31,6 @@ import rx.Completable;
 | 
			
		|||
import rx.Observable;
 | 
			
		||||
import rx.Single;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.ReactiveAdapter.Descriptor;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -48,29 +48,25 @@ public class ReactiveAdapterRegistry {
 | 
			
		|||
	private static final boolean rxJava1Present =
 | 
			
		||||
			ClassUtils.isPresent("rx.Observable", ReactiveAdapterRegistry.class.getClassLoader());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private final Map<Class<?>, ReactiveAdapter> adapterMap = new LinkedHashMap<>();
 | 
			
		||||
	private final Map<Class<?>, ReactiveAdapter> adapterMap = new LinkedHashMap<>(4);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a registry and auto-register default adapters.
 | 
			
		||||
	 */
 | 
			
		||||
	public ReactiveAdapterRegistry() {
 | 
			
		||||
 | 
			
		||||
		// Flux and Mono ahead of Publisher...
 | 
			
		||||
		registerMonoAdapter(Mono.class,
 | 
			
		||||
				source -> (Mono<?>) source, source -> source, new Descriptor(false, true, false));
 | 
			
		||||
 | 
			
		||||
				source -> (Mono<?>) source, source -> source,
 | 
			
		||||
				new ReactiveAdapter.Descriptor(false, true, false));
 | 
			
		||||
		registerFluxAdapter(
 | 
			
		||||
				Flux.class, source -> (Flux<?>) source, source -> source);
 | 
			
		||||
 | 
			
		||||
		registerFluxAdapter(
 | 
			
		||||
				Publisher.class, source -> Flux.from((Publisher<?>) source), source -> source);
 | 
			
		||||
 | 
			
		||||
		registerMonoAdapter(CompletableFuture.class,
 | 
			
		||||
				source -> Mono.fromFuture((CompletableFuture<?>) source),
 | 
			
		||||
				source -> Mono.from((Publisher<?>) source).toFuture(),
 | 
			
		||||
				new Descriptor(false, true, false)
 | 
			
		||||
				source -> Mono.fromFuture((CompletableFuture<?>) source), Mono::toFuture,
 | 
			
		||||
				new ReactiveAdapter.Descriptor(false, true, false)
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		if (rxJava1Present) {
 | 
			
		||||
| 
						 | 
				
			
			@ -84,9 +80,8 @@ public class ReactiveAdapterRegistry {
 | 
			
		|||
	 * functions can assume that input will never be {@code null} and also that
 | 
			
		||||
	 * any {@link Optional} wrapper is unwrapped.
 | 
			
		||||
	 */
 | 
			
		||||
	public void registerMonoAdapter(Class<?> adapteeType,
 | 
			
		||||
			Function<Object, Mono<?>> toAdapter, Function<Mono<?>, Object> fromAdapter,
 | 
			
		||||
			Descriptor descriptor) {
 | 
			
		||||
	public void registerMonoAdapter(Class<?> adapteeType, Function<Object, Mono<?>> toAdapter,
 | 
			
		||||
			Function<Mono<?>, Object> fromAdapter, ReactiveAdapter.Descriptor descriptor) {
 | 
			
		||||
 | 
			
		||||
		this.adapterMap.put(adapteeType, new MonoReactiveAdapter(toAdapter, fromAdapter, descriptor));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +91,8 @@ public class ReactiveAdapterRegistry {
 | 
			
		|||
	 * functions can assume that input will never be {@code null} and also that
 | 
			
		||||
	 * any {@link Optional} wrapper is unwrapped.
 | 
			
		||||
	 */
 | 
			
		||||
	public void registerFluxAdapter(Class<?> adapteeType,
 | 
			
		||||
			Function<Object, Flux<?>> toAdapter, Function<Flux<?>, Object> fromAdapter) {
 | 
			
		||||
	public void registerFluxAdapter(Class<?> adapteeType, Function<Object, Flux<?>> toAdapter,
 | 
			
		||||
			Function<Flux<?>, Object> fromAdapter) {
 | 
			
		||||
 | 
			
		||||
		this.adapterMap.put(adapteeType, new FluxReactiveAdapter(toAdapter, fromAdapter));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +130,14 @@ public class ReactiveAdapterRegistry {
 | 
			
		|||
		return getAdapterInternal(supportedType -> supportedType.equals(actualType));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private ReactiveAdapter getAdapterInternal(Predicate<Class<?>> adapteeTypePredicate) {
 | 
			
		||||
		return this.adapterMap.keySet().stream()
 | 
			
		||||
				.filter(adapteeTypePredicate)
 | 
			
		||||
				.map(this.adapterMap::get)
 | 
			
		||||
				.findFirst()
 | 
			
		||||
				.orElse(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private static Class<?> getActualType(Class<?> adapteeType, Object adaptee) {
 | 
			
		||||
		adaptee = unwrapOptional(adaptee);
 | 
			
		||||
| 
						 | 
				
			
			@ -142,18 +145,7 @@ public class ReactiveAdapterRegistry {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private static Object unwrapOptional(Object value) {
 | 
			
		||||
		if (value != null && value instanceof Optional) {
 | 
			
		||||
			value = ((Optional<?>) value).orElse(null);
 | 
			
		||||
		}
 | 
			
		||||
		return value;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private ReactiveAdapter getAdapterInternal(Predicate<Class<?>> adapteeTypePredicate) {
 | 
			
		||||
		return this.adapterMap.keySet().stream()
 | 
			
		||||
				.filter(adapteeTypePredicate)
 | 
			
		||||
				.map(this.adapterMap::get)
 | 
			
		||||
				.findFirst()
 | 
			
		||||
				.orElse(null);
 | 
			
		||||
		return (value instanceof Optional ? ((Optional<?>) value).orElse(null) : value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -256,25 +248,23 @@ public class ReactiveAdapterRegistry {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private static class RxJava1AdapterRegistrar {
 | 
			
		||||
 | 
			
		||||
		public void register(ReactiveAdapterRegistry registry) {
 | 
			
		||||
 | 
			
		||||
			registry.registerFluxAdapter(Observable.class,
 | 
			
		||||
					source -> RxJava1Adapter.observableToFlux((Observable<?>) source),
 | 
			
		||||
					RxJava1Adapter::publisherToObservable
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			registry.registerMonoAdapter(Single.class,
 | 
			
		||||
					source -> RxJava1Adapter.singleToMono((Single<?>) source),
 | 
			
		||||
					RxJava1Adapter::publisherToSingle,
 | 
			
		||||
					new Descriptor(false, false, false)
 | 
			
		||||
					new ReactiveAdapter.Descriptor(false, false, false)
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			registry.registerMonoAdapter(Completable.class,
 | 
			
		||||
					source -> RxJava1Adapter.completableToMono((Completable) source),
 | 
			
		||||
					RxJava1Adapter::publisherToCompletable,
 | 
			
		||||
					new Descriptor(false, true, true)
 | 
			
		||||
					new ReactiveAdapter.Descriptor(false, true, true)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,10 +30,11 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
 | 
			
		|||
import org.springframework.web.context.request.NativeWebRequest;
 | 
			
		||||
import org.springframework.web.context.request.ServletWebRequest;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertEquals;
 | 
			
		||||
import static org.junit.Assert.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test fixture for {@link ContentNegotiationManagerFactoryBean} tests.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Rossen Stoyanchev
 | 
			
		||||
 */
 | 
			
		||||
public class ContentNegotiationManagerFactoryBeanTests {
 | 
			
		||||
| 
						 | 
				
			
			@ -119,9 +120,7 @@ public class ContentNegotiationManagerFactoryBeanTests {
 | 
			
		|||
		assertEquals(Collections.emptyList(), manager.resolveMediaTypes(this.webRequest));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SPR-10170
 | 
			
		||||
 | 
			
		||||
	@Test(expected = HttpMediaTypeNotAcceptableException.class)
 | 
			
		||||
	@Test(expected = HttpMediaTypeNotAcceptableException.class)  // SPR-10170
 | 
			
		||||
	public void favorPathWithIgnoreUnknownPathExtensionTurnedOff() throws Exception {
 | 
			
		||||
		this.factoryBean.setFavorPathExtension(true);
 | 
			
		||||
		this.factoryBean.setIgnoreUnknownPathExtensions(false);
 | 
			
		||||
| 
						 | 
				
			
			@ -152,9 +151,7 @@ public class ContentNegotiationManagerFactoryBeanTests {
 | 
			
		|||
				manager.resolveMediaTypes(this.webRequest));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SPR-10170
 | 
			
		||||
 | 
			
		||||
	@Test(expected = HttpMediaTypeNotAcceptableException.class)
 | 
			
		||||
	@Test(expected = HttpMediaTypeNotAcceptableException.class)  // SPR-10170
 | 
			
		||||
	public void favorParameterWithUnknownMediaType() throws HttpMediaTypeNotAcceptableException {
 | 
			
		||||
		this.factoryBean.setFavorParameter(true);
 | 
			
		||||
		this.factoryBean.afterPropertiesSet();
 | 
			
		||||
| 
						 | 
				
			
			@ -188,16 +185,12 @@ public class ContentNegotiationManagerFactoryBeanTests {
 | 
			
		|||
				manager.resolveMediaTypes(this.webRequest));
 | 
			
		||||
 | 
			
		||||
		// SPR-10513
 | 
			
		||||
 | 
			
		||||
		this.servletRequest.addHeader("Accept", MediaType.ALL_VALUE);
 | 
			
		||||
 | 
			
		||||
		assertEquals(Collections.singletonList(MediaType.APPLICATION_JSON),
 | 
			
		||||
				manager.resolveMediaTypes(this.webRequest));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SPR-12286
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	@Test  // SPR-12286
 | 
			
		||||
	public void setDefaultContentTypeWithStrategy() throws Exception {
 | 
			
		||||
		this.factoryBean.setDefaultContentTypeStrategy(new FixedContentNegotiationStrategy(MediaType.APPLICATION_JSON));
 | 
			
		||||
		this.factoryBean.afterPropertiesSet();
 | 
			
		||||
| 
						 | 
				
			
			@ -216,7 +209,6 @@ public class ContentNegotiationManagerFactoryBeanTests {
 | 
			
		|||
 | 
			
		||||
		private final Map<String, String> mimeTypes = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		public Map<String, String> getMimeTypes() {
 | 
			
		||||
			return this.mimeTypes;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
		}
 | 
			
		||||
		catch (NoSuchMethodException ex) {
 | 
			
		||||
			// Should never happen
 | 
			
		||||
			throw new IllegalStateException("No handler for HTTP OPTIONS", ex);
 | 
			
		||||
			throw new IllegalStateException("Failed to retrieve internal handler method for HTTP OPTIONS", ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +183,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
	/**
 | 
			
		||||
	 * Iterate all RequestMappingInfo's once again, look if any match by URL at
 | 
			
		||||
	 * least and raise exceptions according to what doesn't match.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @throws HttpRequestMethodNotSupportedException if there are matches by URL
 | 
			
		||||
	 * but not by HTTP method
 | 
			
		||||
	 * @throws HttpMediaTypeNotAcceptableException if there are matches by URL
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +242,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
 | 
			
		||||
		private final List<PartialMatch> partialMatches = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		public PartialMatchHelper(Set<RequestMappingInfo> infos, HttpServletRequest request) {
 | 
			
		||||
			for (RequestMappingInfo info : infos) {
 | 
			
		||||
				if (info.getPatternsCondition().getMatchingCondition(request) != null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +250,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Whether there any partial matches.
 | 
			
		||||
		 */
 | 
			
		||||
| 
						 | 
				
			
			@ -387,20 +384,18 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
 | 
			
		||||
			private final boolean paramsMatch;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * @param info RequestMappingInfo that matches the URL path.
 | 
			
		||||
			 * @param request the current request
 | 
			
		||||
			 */
 | 
			
		||||
			public PartialMatch(RequestMappingInfo info, HttpServletRequest request) {
 | 
			
		||||
				this.info = info;
 | 
			
		||||
				this.methodsMatch = info.getMethodsCondition().getMatchingCondition(request) != null;
 | 
			
		||||
				this.consumesMatch = info.getConsumesCondition().getMatchingCondition(request) != null;
 | 
			
		||||
				this.producesMatch = info.getProducesCondition().getMatchingCondition(request) != null;
 | 
			
		||||
				this.paramsMatch = info.getParamsCondition().getMatchingCondition(request) != null;
 | 
			
		||||
				this.methodsMatch = (info.getMethodsCondition().getMatchingCondition(request) != null);
 | 
			
		||||
				this.consumesMatch = (info.getConsumesCondition().getMatchingCondition(request) != null);
 | 
			
		||||
				this.producesMatch = (info.getProducesCondition().getMatchingCondition(request) != null);
 | 
			
		||||
				this.paramsMatch = (info.getParamsCondition().getMatchingCondition(request) != null);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			public RequestMappingInfo getInfo() {
 | 
			
		||||
				return this.info;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -410,15 +405,15 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			public boolean hasConsumesMatch() {
 | 
			
		||||
				return hasMethodsMatch() && this.consumesMatch;
 | 
			
		||||
				return (hasMethodsMatch() && this.consumesMatch);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			public boolean hasProducesMatch() {
 | 
			
		||||
				return hasConsumesMatch() && this.producesMatch;
 | 
			
		||||
				return (hasConsumesMatch() && this.producesMatch);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			public boolean hasParamsMatch() {
 | 
			
		||||
				return hasProducesMatch() && this.paramsMatch;
 | 
			
		||||
				return (hasProducesMatch() && this.paramsMatch);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -428,6 +423,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Default handler for HTTP OPTIONS.
 | 
			
		||||
	 */
 | 
			
		||||
| 
						 | 
				
			
			@ -435,7 +431,6 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
 | 
			
		|||
 | 
			
		||||
		private final HttpHeaders headers = new HttpHeaders();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		public HttpOptionsHandler(Set<String> declaredMethods) {
 | 
			
		||||
			this.headers.setAllow(initAllowedHttpMethods(declaredMethods));
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue