001package io.jstach.opt.spring.webflux;
002
003import java.nio.charset.StandardCharsets;
004import java.util.List;
005import java.util.Map;
006
007import org.springframework.http.MediaType;
008import org.springframework.web.reactive.result.view.AbstractView;
009import org.springframework.web.reactive.result.view.View;
010import org.springframework.web.server.ServerWebExchange;
011
012import io.jstach.jstache.JStache;
013import io.jstach.jstachio.JStachio;
014import reactor.core.publisher.Mono;
015
016/**
017 * One way to use JStachio with Spring Webflux is to use this special View that will
018 * delegate to JStachio to render.
019 * <p>
020 * By default this view interface will use the static jstachio singleton which normally is
021 * the correct Spring wired version if configured correctly.
022 *
023 * @author agentgt
024 * @author dsyer
025 *
026 */
027public interface JStachioModelView extends View {
028
029        @Override
030        default Mono<Void> render(Map<String, ?> model, MediaType contentType, ServerWebExchange exchange) {
031                if (contentType != null) {
032                        exchange.getResponse().getHeaders().setContentType(contentType);
033                }
034                if (contentType != null) {
035                        exchange.getResponse().getHeaders().setContentType(contentType);
036                }
037
038                View view = new AbstractView() {
039
040                        @Override
041                        protected Mono<Void> renderInternal(Map<String, Object> model, MediaType contentType,
042                                        ServerWebExchange exchange) {
043                                return exchange.getResponse().writeWith(Mono.fromCallable(() -> {
044                                        try {
045                                                byte[] bytes = String.valueOf(jstachio().execute(model())).getBytes(StandardCharsets.UTF_8);
046                                                // just wrapping, no allocation
047                                                return exchange.getResponse().bufferFactory().wrap(bytes);
048                                        }
049                                        catch (Exception ex) {
050                                                throw new IllegalStateException("Failed to render script template", ex);
051                                        }
052                                }));
053                        }
054
055                };
056
057                return view.render(model, contentType, exchange);
058        }
059
060        @Override
061        default List<MediaType> getSupportedMediaTypes() {
062                return List.of(MediaType.TEXT_HTML);
063        }
064
065        /**
066         * Returns the jstachio singleton by default.
067         * @return stachio singleton by default.
068         * @see JStachio#setStaticJStachio(java.util.function.Supplier)
069         */
070        default JStachio jstachio() {
071                return JStachio.of();
072        }
073
074        /**
075         * The model to be rendered by {@link #jstachio()}.
076         * @return model defaulting to <code>this</code> instance.
077         */
078        default Object model() {
079                return this;
080        }
081
082        /**
083         * Creates a spring view from a model
084         * @param model an instance of a class annotated with {@link JStache}.
085         * @return view ready for rendering
086         */
087        public static JStachioModelView of(Object model) {
088                return new JStachioModelView() {
089                        @Override
090                        public Object model() {
091                                return model;
092                        }
093                };
094        }
095
096}