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 */
027@SuppressWarnings("exports")
028public interface JStachioModelView extends View {
029
030        @Override
031        default Mono<Void> render(Map<String, ?> model, MediaType contentType, ServerWebExchange exchange) {
032                if (contentType != null) {
033                        exchange.getResponse().getHeaders().setContentType(contentType);
034                }
035                if (contentType != null) {
036                        exchange.getResponse().getHeaders().setContentType(contentType);
037                }
038
039                View view = new AbstractView() {
040
041                        @Override
042                        protected Mono<Void> renderInternal(Map<String, Object> model, MediaType contentType,
043                                        ServerWebExchange exchange) {
044                                return exchange.getResponse().writeWith(Mono.fromCallable(() -> {
045                                        try {
046                                                byte[] bytes = String.valueOf(jstachio().execute(model())).getBytes(StandardCharsets.UTF_8);
047                                                // just wrapping, no allocation
048                                                return exchange.getResponse().bufferFactory().wrap(bytes);
049                                        }
050                                        catch (Exception ex) {
051                                                throw new IllegalStateException("Failed to render script template", ex);
052                                        }
053                                }));
054                        }
055
056                };
057
058                return view.render(model, contentType, exchange);
059        }
060
061        @Override
062        default List<MediaType> getSupportedMediaTypes() {
063                return List.of(MediaType.TEXT_HTML);
064        }
065
066        /**
067         * Returns the jstachio singleton by default.
068         * @return stachio singleton by default.
069         * @see JStachio#setStatic(java.util.function.Supplier)
070         */
071        default JStachio jstachio() {
072                return JStachio.of();
073        }
074
075        /**
076         * The model to be rendered by {@link #jstachio()}.
077         * @return model defaulting to <code>this</code> instance.
078         */
079        default Object model() {
080                return this;
081        }
082
083        /**
084         * Creates a spring view from a model
085         * @param model an instance of a class annotated with {@link JStache}.
086         * @return view ready for rendering
087         */
088        public static JStachioModelView of(Object model) {
089                return new JStachioModelView() {
090                        @Override
091                        public Object model() {
092                                return model;
093                        }
094                };
095        }
096
097}