001/*
002 * Copyright 2002-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.web.servlet.result;
018
019import java.util.Enumeration;
020import java.util.Map;
021
022import javax.servlet.http.Cookie;
023import javax.servlet.http.HttpServletRequest;
024
025import org.springframework.core.style.ToStringCreator;
026import org.springframework.http.HttpHeaders;
027import org.springframework.mock.web.MockHttpServletRequest;
028import org.springframework.mock.web.MockHttpServletResponse;
029import org.springframework.test.web.servlet.MvcResult;
030import org.springframework.test.web.servlet.ResultHandler;
031import org.springframework.util.LinkedMultiValueMap;
032import org.springframework.util.MultiValueMap;
033import org.springframework.util.ObjectUtils;
034import org.springframework.validation.BindingResult;
035import org.springframework.validation.Errors;
036import org.springframework.web.method.HandlerMethod;
037import org.springframework.web.servlet.FlashMap;
038import org.springframework.web.servlet.HandlerInterceptor;
039import org.springframework.web.servlet.ModelAndView;
040import org.springframework.web.servlet.support.RequestContextUtils;
041
042/**
043 * Result handler that prints {@link MvcResult} details to a given output
044 * stream — for example: {@code System.out}, {@code System.err}, a
045 * custom {@code java.io.PrintWriter}, etc.
046 *
047 * <p>An instance of this class is typically accessed via one of the
048 * {@link MockMvcResultHandlers#print print} or {@link MockMvcResultHandlers#log log}
049 * methods in {@link MockMvcResultHandlers}.
050 *
051 * @author Rossen Stoyanchev
052 * @author Sam Brannen
053 * @since 3.2
054 */
055public class PrintingResultHandler implements ResultHandler {
056
057        private final ResultValuePrinter printer;
058
059
060        /**
061         * Protected constructor.
062         * @param printer a {@link ResultValuePrinter} to do the actual writing
063         */
064        protected PrintingResultHandler(ResultValuePrinter printer) {
065                this.printer = printer;
066        }
067
068        /**
069         * @return the result value printer
070         */
071        protected ResultValuePrinter getPrinter() {
072                return this.printer;
073        }
074
075        /**
076         * Print {@link MvcResult} details.
077         */
078        @Override
079        public final void handle(MvcResult result) throws Exception {
080                this.printer.printHeading("MockHttpServletRequest");
081                printRequest(result.getRequest());
082
083                this.printer.printHeading("Handler");
084                printHandler(result.getHandler(), result.getInterceptors());
085
086                this.printer.printHeading("Async");
087                printAsyncResult(result);
088
089                this.printer.printHeading("Resolved Exception");
090                printResolvedException(result.getResolvedException());
091
092                this.printer.printHeading("ModelAndView");
093                printModelAndView(result.getModelAndView());
094
095                this.printer.printHeading("FlashMap");
096                printFlashMap(RequestContextUtils.getOutputFlashMap(result.getRequest()));
097
098                this.printer.printHeading("MockHttpServletResponse");
099                printResponse(result.getResponse());
100        }
101
102        /**
103         * Print the request.
104         */
105        protected void printRequest(MockHttpServletRequest request) throws Exception {
106                this.printer.printValue("HTTP Method", request.getMethod());
107                this.printer.printValue("Request URI", request.getRequestURI());
108                this.printer.printValue("Parameters", getParamsMultiValueMap(request));
109                this.printer.printValue("Headers", getRequestHeaders(request));
110        }
111
112        protected final HttpHeaders getRequestHeaders(MockHttpServletRequest request) {
113                HttpHeaders headers = new HttpHeaders();
114                Enumeration<?> names = request.getHeaderNames();
115                while (names.hasMoreElements()) {
116                        String name = (String) names.nextElement();
117                        Enumeration<String> values = request.getHeaders(name);
118                        while (values.hasMoreElements()) {
119                                headers.add(name, values.nextElement());
120                        }
121                }
122                return headers;
123        }
124
125        protected final MultiValueMap<String, String> getParamsMultiValueMap(MockHttpServletRequest request) {
126                Map<String, String[]> params = request.getParameterMap();
127                MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<String, String>();
128                for (String name : params.keySet()) {
129                        if (params.get(name) != null) {
130                                for (String value : params.get(name)) {
131                                        multiValueMap.add(name, value);
132                                }
133                        }
134                }
135                return multiValueMap;
136        }
137
138        protected void printAsyncResult(MvcResult result) throws Exception {
139                HttpServletRequest request = result.getRequest();
140                this.printer.printValue("Async started", request.isAsyncStarted());
141                Object asyncResult = null;
142                try {
143                        asyncResult = result.getAsyncResult(0);
144                }
145                catch (IllegalStateException ex) {
146                        // Not set
147                }
148                this.printer.printValue("Async result", asyncResult);
149        }
150
151        /**
152         * Print the handler.
153         */
154        protected void printHandler(Object handler, HandlerInterceptor[] interceptors) throws Exception {
155                if (handler == null) {
156                        this.printer.printValue("Type", null);
157                }
158                else {
159                        if (handler instanceof HandlerMethod) {
160                                HandlerMethod handlerMethod = (HandlerMethod) handler;
161                                this.printer.printValue("Type", handlerMethod.getBeanType().getName());
162                                this.printer.printValue("Method", handlerMethod);
163                        }
164                        else {
165                                this.printer.printValue("Type", handler.getClass().getName());
166                        }
167                }
168        }
169
170        /**
171         * Print exceptions resolved through a HandlerExceptionResolver.
172         */
173        protected void printResolvedException(Exception resolvedException) throws Exception {
174                if (resolvedException == null) {
175                        this.printer.printValue("Type", null);
176                }
177                else {
178                        this.printer.printValue("Type", resolvedException.getClass().getName());
179                }
180        }
181
182        /**
183         * Print the ModelAndView.
184         */
185        protected void printModelAndView(ModelAndView mav) throws Exception {
186                this.printer.printValue("View name", (mav != null) ? mav.getViewName() : null);
187                this.printer.printValue("View", (mav != null) ? mav.getView() : null);
188                if (mav == null || mav.getModel().size() == 0) {
189                        this.printer.printValue("Model", null);
190                }
191                else {
192                        for (String name : mav.getModel().keySet()) {
193                                if (!name.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
194                                        Object value = mav.getModel().get(name);
195                                        this.printer.printValue("Attribute", name);
196                                        this.printer.printValue("value", value);
197                                        Errors errors = (Errors) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name);
198                                        if (errors != null) {
199                                                this.printer.printValue("errors", errors.getAllErrors());
200                                        }
201                                }
202                        }
203                }
204        }
205
206        /**
207         * Print "output" flash attributes.
208         */
209        protected void printFlashMap(FlashMap flashMap) throws Exception {
210                if (ObjectUtils.isEmpty(flashMap)) {
211                        this.printer.printValue("Attributes", null);
212                }
213                else {
214                        for (String name : flashMap.keySet()) {
215                                this.printer.printValue("Attribute", name);
216                                this.printer.printValue("value", flashMap.get(name));
217                        }
218                }
219        }
220
221        /**
222         * Print the response.
223         */
224        protected void printResponse(MockHttpServletResponse response) throws Exception {
225                this.printer.printValue("Status", response.getStatus());
226                this.printer.printValue("Error message", response.getErrorMessage());
227                this.printer.printValue("Headers", getResponseHeaders(response));
228                this.printer.printValue("Content type", response.getContentType());
229                this.printer.printValue("Body", response.getContentAsString());
230                this.printer.printValue("Forwarded URL", response.getForwardedUrl());
231                this.printer.printValue("Redirected URL", response.getRedirectedUrl());
232                printCookies(response.getCookies());
233        }
234
235        /**
236         * Print the supplied cookies in a human-readable form, assuming the
237         * {@link Cookie} implementation does not provide its own {@code toString()}.
238         * @since 4.2
239         */
240        private void printCookies(Cookie[] cookies) {
241                String[] cookieStrings = new String[cookies.length];
242                for (int i = 0; i < cookies.length; i++) {
243                        Cookie cookie = cookies[i];
244                        cookieStrings[i] = new ToStringCreator(cookie)
245                                .append("name", cookie.getName())
246                                .append("value", cookie.getValue())
247                                .append("comment", cookie.getComment())
248                                .append("domain", cookie.getDomain())
249                                .append("maxAge", cookie.getMaxAge())
250                                .append("path", cookie.getPath())
251                                .append("secure", cookie.getSecure())
252                                .append("version", cookie.getVersion())
253                                .append("httpOnly", cookie.isHttpOnly())
254                                .toString();
255                }
256                this.printer.printValue("Cookies", cookieStrings);
257        }
258
259        protected final HttpHeaders getResponseHeaders(MockHttpServletResponse response) {
260                HttpHeaders headers = new HttpHeaders();
261                for (String name : response.getHeaderNames()) {
262                        headers.put(name, response.getHeaders(name));
263                }
264                return headers;
265        }
266
267
268        /**
269         * A contract for how to actually write result information.
270         */
271        protected interface ResultValuePrinter {
272
273                void printHeading(String heading);
274
275                void printValue(String label, Object value);
276        }
277
278}