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

org.thymeleaf.spring5.context.reactive.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-2016, 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.reactive;

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. *

*

* 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. *

*

* 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>
 * 
*

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

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

* Default buffer size to be applied if none is specified. Value = 100. *

*/ public static final int DEFAULT_DATA_DRIVER_BUFFER_SIZE_ELEMENTS = 100; private final Object dataStream; private final int dataStreamBufferSizeElements; private ReactiveAdapterRegistry adapterRegistry; /** *

* 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 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. *

*

* Note the specified 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 Observable * (but not 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); } /** *

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

*

* The specified dataStream must be adaptable to a Reactive Streams * {@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. *

*

* Note the specified 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 Observable * (but not 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) { super(); Validate.notNull(dataStream, "Data stream cannot be null"); Validate.isTrue(dataStreamBufferSizeElements > 0, "Data Buffer Size cannot be <= 0"); this.dataStream = dataStream; this.dataStreamBufferSizeElements = dataStreamBufferSizeElements; } /** *

* Sets the {@link ReactiveAdapterRegistry} used for converting (if necessary) the wrapped asynchronous * object into a {@link Publisher}. *

*

* This method is transparently called before template execution in order * to initialize lazy context variables. It can also be called programmatically, but there is normally * no reason to do this. If not called at all, only {@link Flux} will be allowed as valid type * for the wrapped data stream. *

* * @param reactiveAdapterRegistry the reactive adapter registry. */ public final void setReactiveAdapterRegistry(final ReactiveAdapterRegistry reactiveAdapterRegistry) { // Note the presence of the ReactiveAdapterRegistry is optional, so this method might never be // called. We can only be sure that it will be called if this context variable is part of a model // used for rendering a ThymeleafReactiveView (which, anyway, will be most of the cases). this.adapterRegistry = reactiveAdapterRegistry; } @Override public Publisher getDataStream() { final Publisher publisher = ReactiveContextVariableUtils.computePublisherValue(this.dataStream, this.adapterRegistry); if (!(publisher instanceof Flux)) { throw new IllegalArgumentException( "Reactive Data Driver context variable was set single-valued aynchronous 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; } }