All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.thymeleaf.spring5.context.webflux.ReactiveDataDriverContextVariable Maven / Gradle / Ivy

Go to download

Modern server-side Java template engine for both web and standalone environments

There is a newer version: 3.1.3.RELEASE
Show newest version
/*
 * =============================================================================
 *
 *   Copyright (c) 2011-2018, The THYMELEAF team (http://www.thymeleaf.org)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 * =============================================================================
 */
package org.thymeleaf.spring5.context.webflux;

import org.reactivestreams.Publisher;
import org.springframework.core.ReactiveAdapterRegistry;
import org.thymeleaf.util.Validate;
import reactor.core.publisher.Flux;

/**
 * 

* Basic implementation of the {@link IReactiveDataDriverContextVariable} interface, including also * the extensions specified in {@link IReactiveSSEDataDriverContextVariable}. *

*

* The reactive data stream wrapped by this class will usually have the shape of an implementation of the * {@link Publisher} interface, such as {@link reactor.core.publisher.Flux}. But other types of reactive * artifacts are supported thanks to Spring's {@link org.springframework.core.ReactiveAdapterRegistry} * mechanism if such adapter registry has been set in the context * (see {@link SpringWebFluxContext#getReactiveAdapterRegistry()}). *

*

* Data-driver context variables are required to be multi-valued. * Being multi-valued does not mean to necessarily return more than one value, * but simply to have the capability to do so. E.g. a {@link reactor.core.publisher.Flux} object will * be considered multi-valued even if it publishes none or just one result, whereas a * {@link reactor.core.publisher.Mono} object will be considered single-valued. *

*

* Example use: *

*

 * @RequestMapping("/something")
 * public String doSomething(final Model model) {
 *     final Publisher<Item> data = ...; // This has to be MULTI-VALUED (e.g. Flux)
 *     model.addAttribute("data", new ReactiveDataDriverContextVariable(data, 100));
 *     return "view";
 * }
 * 
*

* And then at the template: *

*

 * <table>
 *   <tbody>
 *     <tr th:each="item : ${data}">
 *       <td th:text="${item}">some item...</td>
 *     </tr>
 *   </tbody>
 * </table>
 * 
*

* For more information on the way this class would work in SSE (Server-Sent Event) scenarios, see * {@link IReactiveSSEDataDriverContextVariable}. *

*

* This class is NOT thread-safe. Thread-safety is not a requirement for context variables. *

* * @see IReactiveDataDriverContextVariable * @see IReactiveSSEDataDriverContextVariable * * @author Daniel Fernández * * @since 3.0.3 * */ public class ReactiveDataDriverContextVariable implements IReactiveSSEDataDriverContextVariable { /** *

* Default buffer size to be applied if none is specified. Value = {@code 10}. *

*/ public static final int DEFAULT_DATA_DRIVER_BUFFER_SIZE_ELEMENTS = 10; /** *

* Default value for the first event ID (for SSE scenarios). Value = {@code 0}. *

*/ public static final long DEFAULT_FIRST_EVENT_ID = 0L; private final Object dataStream; private final int dataStreamBufferSizeElements; private final String sseEventsPrefix; private final long sseEventsFirstID; /** *

* Creates a new lazy context variable, wrapping a reactive asynchronous data stream. *

*

* Buffer size will be set to {@link #DEFAULT_DATA_DRIVER_BUFFER_SIZE_ELEMENTS}. *

*

* The specified {@code dataStream} must be adaptable to a Reactive Stream's * {@link Publisher} by means of Spring's {@link ReactiveAdapterRegistry} mechanism. If no * adapter has been registered for the type of the asynchronous object, and exception will be * thrown during lazy resolution. If no adapter registry has been set into the context * (see {@link SpringWebFluxContext#getReactiveAdapterRegistry()}) this data stream must mandatorily * be a {@link Flux}. *

*

* Note the specified {@code dataStream} must be multi-valued. *

*

* Examples of supported implementations are Reactor's {@link Flux} (but not * {@link reactor.core.publisher.Mono}), and also RxJava's {@code Observable} * (but not {@code Single}). *

* * @param dataStream the asynchronous object, which must be convertible to a multi-valued {@link Publisher} by * means of Spring's {@link ReactiveAdapterRegistry}. */ public ReactiveDataDriverContextVariable(final Object dataStream) { this(dataStream, DEFAULT_DATA_DRIVER_BUFFER_SIZE_ELEMENTS, null, DEFAULT_FIRST_EVENT_ID); } /** *

* Creates a new lazy context variable, wrapping a reactive asynchronous data stream and specifying a * buffer size. *

*

* The specified {@code dataStream} must be adaptable to a Reactive Stream's * {@link Publisher} by means of Spring's {@link ReactiveAdapterRegistry} mechanism. If no * adapter has been registered for the type of the asynchronous object, and exception will be * thrown during lazy resolution. If no adapter registry has been set into the context * (see {@link SpringWebFluxContext#getReactiveAdapterRegistry()}) this data stream must mandatorily * be a {@link Flux}. *

*

* Note the specified {@code dataStream} must be multi-valued. *

*

* Examples of supported implementations are Reactor's {@link Flux} (but not * {@link reactor.core.publisher.Mono}), and also RxJava's {@code Observable} * (but not {@code Single}). *

* * @param dataStream the asynchronous object, which must be convertible to a multi-valued {@link Publisher} by * means of Spring's {@link ReactiveAdapterRegistry}. * @param dataStreamBufferSizeElements the buffer size to be applied (in elements). */ public ReactiveDataDriverContextVariable(final Object dataStream, final int dataStreamBufferSizeElements) { this(dataStream, dataStreamBufferSizeElements, null, DEFAULT_FIRST_EVENT_ID); } /** *

* Creates a new lazy context variable, wrapping a reactive asynchronous data stream and specifying a * buffer size and a prefix for all the names and IDs of events generated from a specific SSE stream. *

*

* The specified {@code dataStream} must be adaptable to a Reactive Stream's * {@link Publisher} by means of Spring's {@link ReactiveAdapterRegistry} mechanism. If no * adapter has been registered for the type of the asynchronous object, and exception will be * thrown during lazy resolution. If no adapter registry has been set into the context * (see {@link SpringWebFluxContext#getReactiveAdapterRegistry()}) this data stream must mandatorily * be a {@link Flux}. *

*

* Note the specified {@code dataStream} must be multi-valued. *

*

* Examples of supported implementations are Reactor's {@link Flux} (but not * {@link reactor.core.publisher.Mono}), and also RxJava's {@code Observable} * (but not {@code Single}). *

* * @param dataStream the asynchronous object, which must be convertible to a multi-valued {@link Publisher} by * means of Spring's {@link ReactiveAdapterRegistry}. * @param dataStreamBufferSizeElements the buffer size to be applied (in elements). * @param sseEventsPrefix the prefix to be used for event names and IDs, so that events coming from a specific * SSE stream can be identified (if applies). Can be null. * * @since 3.0.8 */ public ReactiveDataDriverContextVariable( final Object dataStream, final int dataStreamBufferSizeElements, final String sseEventsPrefix) { this(dataStream, dataStreamBufferSizeElements, sseEventsPrefix, DEFAULT_FIRST_EVENT_ID); } /** *

* Creates a new lazy context variable, wrapping a reactive asynchronous data stream and specifying a * buffer size and a value for the ID of the first event generated in SSE scenarios. *

*

* The specified {@code dataStream} must be adaptable to a Reactive Stream's * {@link Publisher} by means of Spring's {@link ReactiveAdapterRegistry} mechanism. If no * adapter has been registered for the type of the asynchronous object, and exception will be * thrown during lazy resolution. If no adapter registry has been set into the context * (see {@link SpringWebFluxContext#getReactiveAdapterRegistry()}) this data stream must mandatorily * be a {@link Flux}. *

*

* Note the specified {@code dataStream} must be multi-valued. *

*

* Examples of supported implementations are Reactor's {@link Flux} (but not * {@link reactor.core.publisher.Mono}), and also RxJava's {@code Observable} * (but not {@code Single}). *

* * @param dataStream the asynchronous object, which must be convertible to a multi-valued {@link Publisher} by * means of Spring's {@link ReactiveAdapterRegistry}. * @param dataStreamBufferSizeElements the buffer size to be applied (in elements). * @param sseEventsFirstID the first value to be used as event ID in SSE scenarios (if applies). * * @since 3.0.4 */ public ReactiveDataDriverContextVariable( final Object dataStream, final int dataStreamBufferSizeElements, final long sseEventsFirstID) { this(dataStream, dataStreamBufferSizeElements, null, sseEventsFirstID); } /** *

* Creates a new lazy context variable, wrapping a reactive asynchronous data stream and specifying a * buffer size and a value for the ID of the first event generated in SSE scenarios and a prefix for all * the names and IDs of events generated from a specific SSE stream. *

*

* The specified {@code dataStream} must be adaptable to a Reactive Stream's * {@link Publisher} by means of Spring's {@link ReactiveAdapterRegistry} mechanism. If no * adapter has been registered for the type of the asynchronous object, and exception will be * thrown during lazy resolution. If no adapter registry has been set into the context * (see {@link SpringWebFluxContext#getReactiveAdapterRegistry()}) this data stream must mandatorily * be a {@link Flux}. *

*

* Note the specified {@code dataStream} must be multi-valued. *

*

* Examples of supported implementations are Reactor's {@link Flux} (but not * {@link reactor.core.publisher.Mono}), and also RxJava's {@code Observable} * (but not {@code Single}). *

* * @param dataStream the asynchronous object, which must be convertible to a multi-valued {@link Publisher} by * means of Spring's {@link ReactiveAdapterRegistry}. * @param dataStreamBufferSizeElements the buffer size to be applied (in elements). * @param sseEventsPrefix the prefix to be used for event names and IDs, so that events coming from a specific * SSE stream can be identified (if applies). Can be null. * @param sseEventsFirstID the first value to be used as event ID in SSE scenarios (if applies). * * @since 3.0.8 */ public ReactiveDataDriverContextVariable( final Object dataStream, final int dataStreamBufferSizeElements, final String sseEventsPrefix, final long sseEventsFirstID) { super(); Validate.notNull(dataStream, "Data stream cannot be null"); Validate.isTrue(dataStreamBufferSizeElements > 0, "Data Buffer Size cannot be <= 0"); // The prefix for SSE events CAN be null Validate.isTrue(sseEventsFirstID >= 0L, "First Event ID cannot be < 0"); this.dataStream = dataStream; this.dataStreamBufferSizeElements = dataStreamBufferSizeElements; this.sseEventsPrefix = sseEventsPrefix; this.sseEventsFirstID = sseEventsFirstID; } @Override public Publisher getDataStream(final ReactiveAdapterRegistry reactiveAdapterRegistry) { final Publisher publisher = ReactiveContextVariableUtils.computePublisherValue(this.dataStream, reactiveAdapterRegistry); if (!(publisher instanceof Flux)) { throw new IllegalArgumentException( "Reactive Data Driver context variable was set single-valued asynchronous object. But data driver " + "variables must wrap multi-valued data streams (so that they can be iterated at the template"); } return publisher; } @Override public final int getBufferSizeElements() { return this.dataStreamBufferSizeElements; } @Override public final String getSseEventsPrefix() { return this.sseEventsPrefix; } @Override public final long getSseEventsFirstID() { return this.sseEventsFirstID; } }