use of org.springframework.core.ReactiveAdapter in project spring-framework by spring-projects.
the class HandlerMethodArgumentResolverSupport method checkParameterType.
/**
* 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);
}
use of org.springframework.core.ReactiveAdapter in project spring-framework by spring-projects.
the class BodyInserters method fromProducer.
/**
* Inserter to write the given producer of value(s) which must be a {@link Publisher}
* or another producer adaptable to a {@code Publisher} via
* {@link ReactiveAdapterRegistry}.
* <p>Alternatively, consider using the {@code body} shortcuts on
* {@link org.springframework.web.reactive.function.client.WebClient WebClient} and
* {@link org.springframework.web.reactive.function.server.ServerResponse ServerResponse}.
* @param <T> the type of the body
* @param producer the source of body value(s).
* @param elementClass the class of values to be produced
* @return the inserter to write a producer
* @since 5.2
*/
public static <T> BodyInserter<T, ReactiveHttpOutputMessage> fromProducer(T producer, Class<?> elementClass) {
Assert.notNull(producer, "'producer' must not be null");
Assert.notNull(elementClass, "'elementClass' must not be null");
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(producer.getClass());
Assert.notNull(adapter, "'producer' type is unknown to ReactiveAdapterRegistry");
return (message, context) -> writeWithMessageWriters(message, context, producer, ResolvableType.forClass(elementClass), adapter);
}
use of org.springframework.core.ReactiveAdapter in project spring-framework by spring-projects.
the class BodyInserters method writeWithMessageWriters.
private static <M extends ReactiveHttpOutputMessage> Mono<Void> writeWithMessageWriters(M outputMessage, BodyInserter.Context context, Object body, ResolvableType bodyType, @Nullable ReactiveAdapter adapter) {
Publisher<?> publisher;
if (body instanceof Publisher) {
publisher = (Publisher<?>) body;
} else if (adapter != null) {
publisher = adapter.toPublisher(body);
} else {
publisher = Mono.just(body);
}
MediaType mediaType = outputMessage.getHeaders().getContentType();
return context.messageWriters().stream().filter(messageWriter -> messageWriter.canWrite(bodyType, mediaType)).findFirst().map(BodyInserters::cast).map(writer -> write(publisher, bodyType, mediaType, outputMessage, context, writer)).orElseGet(() -> Mono.error(unsupportedError(bodyType, context, mediaType)));
}
use of org.springframework.core.ReactiveAdapter in project spring-framework by spring-projects.
the class AbstractMessageWriterResultHandler method writeBody.
/**
* Write a given body to the response with {@link HttpMessageWriter}.
* @param body the object to write
* @param bodyParameter the {@link MethodParameter} of the body to write
* @param actualParam the actual return type of the method that returned the value;
* could be different from {@code bodyParameter} when processing {@code HttpEntity}
* for example
* @param exchange the current exchange
* @return indicates completion or error
* @since 5.0.2
*/
@SuppressWarnings({ "unchecked", "rawtypes", "ConstantConditions" })
protected Mono<Void> writeBody(@Nullable Object body, MethodParameter bodyParameter, @Nullable MethodParameter actualParam, ServerWebExchange exchange) {
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
ResolvableType actualType = (actualParam != null ? ResolvableType.forMethodParameter(actualParam) : bodyType);
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyType.resolve(), body);
Publisher<?> publisher;
ResolvableType elementType;
ResolvableType actualElementType;
if (adapter != null) {
publisher = adapter.toPublisher(body);
boolean isUnwrapped = KotlinDetector.isSuspendingFunction(bodyParameter.getMethod()) && !COROUTINES_FLOW_CLASS_NAME.equals(bodyType.toClass().getName());
ResolvableType genericType = isUnwrapped ? bodyType : bodyType.getGeneric();
elementType = getElementType(adapter, genericType);
actualElementType = elementType;
} else {
publisher = Mono.justOrEmpty(body);
actualElementType = body != null ? ResolvableType.forInstance(body) : bodyType;
elementType = (bodyType.toClass() == Object.class && body != null ? actualElementType : bodyType);
}
if (elementType.resolve() == void.class || elementType.resolve() == Void.class) {
return Mono.from((Publisher<Void>) publisher);
}
MediaType bestMediaType;
try {
bestMediaType = selectMediaType(exchange, () -> getMediaTypesFor(elementType));
} catch (NotAcceptableStatusException ex) {
HttpStatus statusCode = exchange.getResponse().getStatusCode();
if (statusCode != null && statusCode.isError()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring error response content (if any). " + ex.getReason());
}
return Mono.empty();
}
throw ex;
}
if (bestMediaType != null) {
String logPrefix = exchange.getLogPrefix();
if (logger.isDebugEnabled()) {
logger.debug(logPrefix + (publisher instanceof Mono ? "0..1" : "0..N") + " [" + elementType + "]");
}
for (HttpMessageWriter<?> writer : getMessageWriters()) {
if (writer.canWrite(actualElementType, bestMediaType)) {
return writer.write((Publisher) publisher, actualType, elementType, bestMediaType, exchange.getRequest(), exchange.getResponse(), Hints.from(Hints.LOG_PREFIX_HINT, logPrefix));
}
}
}
MediaType contentType = exchange.getResponse().getHeaders().getContentType();
boolean isPresentMediaType = (contentType != null && contentType.equals(bestMediaType));
Set<MediaType> producibleTypes = exchange.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isPresentMediaType || !CollectionUtils.isEmpty(producibleTypes)) {
return Mono.error(new HttpMessageNotWritableException("No Encoder for [" + elementType + "] with preset Content-Type '" + contentType + "'"));
}
List<MediaType> mediaTypes = getMediaTypesFor(elementType);
if (bestMediaType == null && mediaTypes.isEmpty()) {
return Mono.error(new IllegalStateException("No HttpMessageWriter for " + elementType));
}
return Mono.error(new NotAcceptableStatusException(mediaTypes));
}
use of org.springframework.core.ReactiveAdapter in project spring-framework by spring-projects.
the class ReactiveTypeHandler method handleValue.
/**
* Process the given reactive return value and decide whether to adapt it
* to a {@link ResponseBodyEmitter} or a {@link DeferredResult}.
* @return an emitter for streaming, or {@code null} if handled internally
* with a {@link DeferredResult}
*/
@Nullable
public ResponseBodyEmitter handleValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mav, NativeWebRequest request) throws Exception {
Assert.notNull(returnValue, "Expected return value");
ReactiveAdapter adapter = this.adapterRegistry.getAdapter(returnValue.getClass());
Assert.state(adapter != null, () -> "Unexpected return value: " + returnValue);
ResolvableType elementType = ResolvableType.forMethodParameter(returnType).getGeneric();
Class<?> elementClass = elementType.toClass();
Collection<MediaType> mediaTypes = getMediaTypes(request);
Optional<MediaType> mediaType = mediaTypes.stream().filter(MimeType::isConcrete).findFirst();
if (adapter.isMultiValue()) {
if (mediaTypes.stream().anyMatch(MediaType.TEXT_EVENT_STREAM::includes) || ServerSentEvent.class.isAssignableFrom(elementClass)) {
logExecutorWarning(returnType);
SseEmitter emitter = new SseEmitter(STREAMING_TIMEOUT_VALUE);
new SseEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
return emitter;
}
if (CharSequence.class.isAssignableFrom(elementClass)) {
logExecutorWarning(returnType);
ResponseBodyEmitter emitter = getEmitter(mediaType.orElse(MediaType.TEXT_PLAIN));
new TextEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
return emitter;
}
for (MediaType type : mediaTypes) {
for (MediaType streamingType : JSON_STREAMING_MEDIA_TYPES) {
if (streamingType.includes(type)) {
logExecutorWarning(returnType);
ResponseBodyEmitter emitter = getEmitter(streamingType);
new JsonEmitterSubscriber(emitter, this.taskExecutor).connect(adapter, returnValue);
return emitter;
}
}
}
}
// Not streaming...
DeferredResult<Object> result = new DeferredResult<>();
new DeferredResultSubscriber(result, adapter, elementType).connect(adapter, returnValue);
WebAsyncUtils.getAsyncManager(request).startDeferredResultProcessing(result, mav);
return null;
}
Aggregations