diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java index bb0b7ad72a..a829562f2d 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapter.java @@ -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}. * - *

An adapter supports a specific adaptee type whose stream semantics can be - * checked via {@link #getDescriptor()}. + *

An adapter supports a specific adaptee type whose stream semantics + * can be checked via {@link #getDescriptor()}. * *

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; } - } } diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java index 921dae625f..06d9edeba0 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java @@ -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, ReactiveAdapter> adapterMap = new LinkedHashMap<>(); + private final Map, 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> toAdapter, Function, Object> fromAdapter, - Descriptor descriptor) { + public void registerMonoAdapter(Class adapteeType, Function> toAdapter, + Function, 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> toAdapter, Function, Object> fromAdapter) { + public void registerFluxAdapter(Class adapteeType, Function> toAdapter, + Function, 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> 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> 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) ); } } diff --git a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java index e9d95c2a13..baabc76449 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java @@ -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 mimeTypes = new HashMap<>(); - public Map getMimeTypes() { return this.mimeTypes; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java index c404e7fbe7..079b020fd3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java @@ -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 partialMatches = new ArrayList<>(); - public PartialMatchHelper(Set 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 declaredMethods) { this.headers.setAllow(initAllowedHttpMethods(declaredMethods)); }