001package io.jstach.opt.spring.example.hello;
002
003import java.util.Map;
004
005import org.springframework.http.ResponseEntity;
006import org.springframework.stereotype.Controller;
007import org.springframework.web.bind.annotation.GetMapping;
008import org.springframework.web.bind.annotation.RequestParam;
009import org.springframework.web.bind.annotation.ResponseBody;
010import org.springframework.web.servlet.ModelAndView;
011import org.springframework.web.servlet.View;
012
013import io.jstach.jstache.JStacheInterfaces;
014import io.jstach.jstachio.JStachio;
015import io.jstach.jstachio.Template;
016import io.jstach.jstachio.TemplateModel;
017import io.jstach.jstachio.context.ContextNode;
018import io.jstach.opt.spring.web.JStachioHttpMessageConverter;
019import io.jstach.opt.spring.webmvc.JStachioModelView;
020
021/**
022 * Example hello world controller using different ways to use JStachio for web
023 * development.
024 *
025 * @author agentgt
026 */
027@Controller
028public class HelloController {
029
030        /**
031         * Placate JDK 18 Javadoc
032         * @param jstachio spring powered jstachio
033         * @param view wired in template
034         */
035        public HelloController(JStachio jstachio, Template<HelloModel> view) {
036                this.jstachio = jstachio;
037                this.view = view;
038        }
039
040        /**
041         * (Optional) Spring will inject this template as the templates are either component
042         * scanned or loaded by the ServiceLoader into Spring's context.
043         *
044         * This is usually not needed as just returning the models is good enough.
045         */
046        public final Template<HelloModel> view;
047
048        /**
049         * Although not needed You can also wire in JStachio directly
050         */
051        public final JStachio jstachio;
052
053        /**
054         * Here we use JStachio runtime to resolve the renderer (in this case we are calling
055         * them Views) via Springs Http Message Converter.
056         * @apiNote Notice that the method has to be annotated with
057         * <code>&#64;ResponseBody</code>.
058         * @return the model that will be used to find the correct view and then rendered
059         * using that view
060         * @see JStachioHttpMessageConverter
061         */
062        @GetMapping(value = "/")
063        @ResponseBody
064        public HelloModel hello() {
065                return new HelloModel("Spring Boot is now JStachioed!");
066        }
067
068        /**
069         * Here we use the generated code directly and return a {@link TemplateModel} which is
070         * analogous to {@link ModelAndView}.
071         *
072         * @apiNote Notice that the method has to be annotated with
073         * <code>&#64;ResponseBody</code>.
074         * @return the template model pair that already has the template found.
075         * @see JStachioHttpMessageConverter
076         */
077        @GetMapping(value = "/templateModel")
078        @ResponseBody
079        public TemplateModel templateModel() {
080                return HelloModelView.of().model(new HelloModel("Spring Boot is using JStachio TemplateModel!"));
081        }
082
083        /**
084         * Here we use a {@link ResponseEntity} which allows use to set status codes with our
085         * model to be rendered.
086         * @return a response entity.
087         * @see JStachioHttpMessageConverter
088         */
089        @GetMapping(value = "/responseEntity")
090        public ResponseEntity<HelloModel> entity() {
091                return ResponseEntity.badRequest().body(new HelloModel("Spring Boot is using JStachio ResponseEntity. "
092                                + "This is a 400 http error code but is not an actual error!"));
093        }
094
095        /**
096         * Here we could use {@link JStacheInterfaces} to make our model implement
097         * {@link JStachioModelView} to support the traditional servlet MVC approach. The
098         * model will use the static jstachio singleton that will be the spring one.
099         * <p>
100         * This approach has pros and cons. It makes your models slightly coupled to Spring
101         * MVC but allows you to return different views if say you had to redirect on some
102         * inputs ({@link org.springframework.web.servlet.view.RedirectView}).
103         *
104         * @apiNote Notice that the return type is {@link View}.
105         * @return the model and view that will be used as View (see
106         * {@link HelloModelAndView}).
107         * @see JStachioHttpMessageConverter
108         * @see HelloModelAndView
109         */
110        @GetMapping(value = "/mvc")
111        @SuppressWarnings("exports")
112        public View mvc() {
113                return new HelloModelAndView("Spring Boot MVC is now JStachioed!");
114        }
115
116        /**
117         * Here we show MVC model support which will be bound to
118         * {@value ContextNode#CONTEXT_BINDING_NAME}.
119         * @param model Spring provided model map to be filled
120         * @return the model and view
121         */
122        @GetMapping(value = "/context")
123        @SuppressWarnings("exports")
124        public View context(Map<String, Object> model) {
125                model.put("csrf", "CSRF_TOKEN");
126                return new HelloModelAndView("Spring Boot MVC is now JStachioed with csrf!");
127        }
128
129        /**
130         * Here we use the {@linkplain #view wired renderer}.
131         * @return template model pair derived from {@link Template#model(Object)}.
132         */
133        @GetMapping(value = "/wired")
134        @ResponseBody
135        public TemplateModel wired() {
136                var model = new HelloModel("JStachioed is wired!");
137                return view.model(model);
138        }
139
140        /**
141         * Here we could use {@link JStacheInterfaces} to make our model implement
142         * {@link JStachioModelView} to support the traditional servlet MVC approach. The
143         * model will use the static jstachio singleton that will be the spring one.
144         * <p>
145         * This approach has pros and cons. It makes your models slightly coupled to Spring
146         * MVC but allows you to return different views if say you had to redirect on some
147         * inputs ({@link org.springframework.web.servlet.view.RedirectView}).
148         * <p>
149         *
150         * This controller method is to test buffering. Ignore.
151         *
152         * @apiNote Notice that the return type is {@link View}.
153         * @param size size of output
154         * @return the model and view that will be used as View (see
155         * {@link HelloModelAndView}).
156         * @see JStachioHttpMessageConverter
157         * @see HelloModelAndView
158         * @hidden
159         */
160        @GetMapping(value = "/long/mvc")
161        @SuppressWarnings("exports")
162        public View longMvc(@RequestParam(name = "size", defaultValue = "0") int size) {
163                if (size <= 0) {
164                        size = 1024 * 16;
165                }
166                StringBuilder sb = new StringBuilder();
167                for (int i = 0; i < size; i++) {
168                        int j = (i % 64) + 48;
169                        char c = (char) j;
170                        sb.append(c);
171                }
172                return new HelloModelAndView(sb.toString());
173        }
174
175}