@Override public boolean supportsReturnType(MethodParameter returnType) { return this.adapterRegistry.getAdapter(returnType.getParameterType()) != null; }
/** * Get the adapter for the given reactive type. */ @Nullable public ReactiveAdapter getAdapter(Class<?> reactiveType) { return getAdapter(reactiveType, null); }
/** * Get the adapter for the given reactive type. */ @Nullable public ReactiveAdapter getAdapter(Class<?> reactiveType) { return getAdapter(reactiveType, null); }
/** * Whether the type can be adapted to a Reactive Streams {@link Publisher}. */ public boolean isReactiveType(Class<?> type) { return (this.reactiveRegistry.hasAdapters() && this.reactiveRegistry.getAdapter(type) != null); }
private ReactiveAdapter getAdapter(Class<?> reactiveType) { return this.registry.getAdapter(reactiveType); }
@Override public ListenableFuture<?> toListenableFuture(Object returnValue, MethodParameter returnType) { ReactiveAdapter adapter = this.adapterRegistry.getAdapter(returnType.getParameterType(), returnValue); Assert.state(adapter != null, () -> "No ReactiveAdapter found for " + returnType.getParameterType()); return new MonoToListenableFutureAdapter<>(Mono.from(adapter.toPublisher(returnValue))); }
@Override public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { ReactiveAdapter adapter = this.adapterRegistry.getAdapter(returnType.getParameterType(), returnValue); return (adapter != null && !adapter.isMultiValue() && !adapter.isNoValue()); }
/** * Whether the type can be adapted to a Reactive Streams {@link Publisher}. */ public boolean isReactiveType(Class<?> type) { return (this.reactiveRegistry.hasAdapters() && this.reactiveRegistry.getAdapter(type) != null); }
@Nullable private Object findAndRemoveReactiveAttribute(Model model, String attributeName) { return model.asMap().entrySet().stream() .filter(entry -> { if (!entry.getKey().startsWith(attributeName)) { return false; } ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, entry.getValue()); if (adapter == null) { return false; } String name = attributeName + ClassUtils.getShortName(adapter.getReactiveType()); return entry.getKey().equals(name); }) .findFirst() .map(entry -> { // Remove since we will be re-inserting the resolved attribute value model.asMap().remove(entry.getKey()); return entry.getValue(); }) .orElse(null); }
@Override public Mono<Object> resolveArgument( MethodParameter parameter, BindingContext context, ServerWebExchange exchange) { Mono<Principal> principal = exchange.getPrincipal(); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType()); return (adapter != null ? Mono.just(adapter.fromPublisher(principal)) : Mono.from(principal)); }
@Override public Mono<Object> resolveArgument( MethodParameter parameter, BindingContext context, ServerWebExchange exchange) { Mono<WebSession> session = exchange.getSession(); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType()); return (adapter != null ? Mono.just(adapter.fromPublisher(session)) : Mono.from(session)); }
/** * Evaluate the {@code Predicate} on the method parameter type or on * the generic type within a reactive type wrapper. */ protected boolean checkParameterType(MethodParameter parameter, Predicate<Class<?>> predicate) { Class<?> type = parameter.getParameterType(); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type); if (adapter != null) { assertHasValues(adapter, parameter); type = parameter.nested().getNestedParameterType(); } return predicate.test(type); }
@Override protected Object resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) { Object value = exchange.getAttribute(name); ReactiveAdapter toAdapter = getAdapterRegistry().getAdapter(parameter.getParameterType()); if (toAdapter != null) { if (value == null) { Assert.isTrue(toAdapter.supportsEmpty(), () -> "No request attribute '" + name + "' and target type " + parameter.getGenericParameterType() + " doesn't support empty values."); return toAdapter.fromPublisher(Mono.empty()); } if (parameter.getParameterType().isAssignableFrom(value.getClass())) { return value; } ReactiveAdapter fromAdapter = getAdapterRegistry().getAdapter(value.getClass()); Assert.isTrue(fromAdapter != null, () -> getClass().getSimpleName() + " doesn't support " + "reactive type wrapper: " + parameter.getGenericParameterType()); return toAdapter.fromPublisher(fromAdapter.toPublisher(value)); } return value; }
/** * Get a {@code ReactiveAdapter} for the top-level return value type. * @return the matching adapter, or {@code null} if none */ @Nullable protected ReactiveAdapter getAdapter(HandlerResult result) { return getAdapterRegistry().getAdapter(result.getReturnType().resolve(), result.getReturnValue()); }
/** * Evaluate the {@code Predicate} on the method parameter type but raise an * {@code IllegalStateException} if the same matches the generic type * within a reactive type wrapper. */ protected boolean checkParameterTypeNoReactiveWrapper(MethodParameter parameter, Predicate<Class<?>> predicate) { Class<?> type = parameter.getParameterType(); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type); if (adapter != null) { assertHasValues(adapter, parameter); type = parameter.nested().getNestedParameterType(); } if (predicate.test(type)) { if (adapter == null) { return true; } throw buildReactiveWrapperException(parameter); } return false; }
private Object getErrors(MethodParameter parameter, BindingContext context) { Assert.isTrue(parameter.getParameterIndex() > 0, "Errors argument must be declared immediately after a model attribute argument"); int index = parameter.getParameterIndex() - 1; MethodParameter attributeParam = MethodParameter.forExecutable(parameter.getExecutable(), index); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(attributeParam.getParameterType()); Assert.state(adapter == null, "An @ModelAttribute and an Errors/BindingResult argument " + "cannot both be declared with an async type wrapper. " + "Either declare the @ModelAttribute without an async wrapper type or " + "handle a WebExchangeBindException error signal through the async type."); ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); String name = (ann != null && StringUtils.hasText(ann.value()) ? ann.value() : Conventions.getVariableNameForParameter(attributeParam)); Object errors = context.getModel().asMap().get(BindingResult.MODEL_KEY_PREFIX + name); Assert.state(errors != null, () -> "An Errors/BindingResult argument is expected " + "immediately after the @ModelAttribute argument to which it applies. " + "For @RequestBody and @RequestPart arguments, please declare them with a reactive " + "type wrapper and use its onError operators to handle WebExchangeBindException: " + parameter.getMethod()); return errors; }
private Mono<Void> handleResult(HandlerResult handlerResult, BindingContext bindingContext) { Object value = handlerResult.getReturnValue(); if (value != null) { ResolvableType type = handlerResult.getReturnType(); ReactiveAdapter adapter = this.adapterRegistry.getAdapter(type.resolve(), value); if (isAsyncVoidType(type, adapter)) { return Mono.from(adapter.toPublisher(value)); } String name = getAttributeName(handlerResult.getReturnTypeSource()); bindingContext.getModel().asMap().putIfAbsent(name, value); } return Mono.empty(); }
private Mono<?> prepareAttributeMono(String attributeName, ResolvableType attributeType, BindingContext context, ServerWebExchange exchange) { Object attribute = context.getModel().asMap().get(attributeName); if (attribute == null) { attribute = findAndRemoveReactiveAttribute(context.getModel(), attributeName); } if (attribute == null) { return createAttribute(attributeName, attributeType.toClass(), context, exchange); } ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute); if (adapterFrom != null) { Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding only supports single-value async types"); return Mono.from(adapterFrom.toPublisher(attribute)); } else { return Mono.justOrEmpty(attribute); } }
/** * Evaluate the {@code Predicate} on the method parameter type if it has the * given annotation, nesting within {@link java.util.Optional} if necessary, * but raise an {@code IllegalStateException} if the same matches the generic * type within a reactive type wrapper. */ protected <A extends Annotation> boolean checkAnnotatedParamNoReactiveWrapper( MethodParameter parameter, Class<A> annotationType, BiPredicate<A, Class<?>> typePredicate) { A annotation = parameter.getParameterAnnotation(annotationType); if (annotation == null) { return false; } parameter = parameter.nestedIfOptional(); Class<?> type = parameter.getNestedParameterType(); ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type); if (adapter != null) { assertHasValues(adapter, parameter); parameter = parameter.nested(); type = parameter.getNestedParameterType(); } if (typePredicate.test(annotation, type)) { if (adapter == null) { return true; } throw buildReactiveWrapperException(parameter); } return false; }
ReactiveAdapter adapter = (resolvedType != null ? getAdapterRegistry().getAdapter(resolvedType) : null); ResolvableType valueType = (adapter != null ? type.getGeneric() : type);