001/*
002 * Copyright 2002-2018 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.web.servlet.mvc.method.annotation;
018
019import java.io.IOException;
020import java.io.OutputStream;
021import java.io.Writer;
022
023import javax.servlet.ServletResponse;
024
025import org.springframework.core.MethodParameter;
026import org.springframework.lang.Nullable;
027import org.springframework.web.bind.support.WebDataBinderFactory;
028import org.springframework.web.context.request.NativeWebRequest;
029import org.springframework.web.method.support.HandlerMethodArgumentResolver;
030import org.springframework.web.method.support.ModelAndViewContainer;
031
032/**
033 * Resolves servlet backed response-related method arguments. Supports values of the
034 * following types:
035 * <ul>
036 * <li>{@link ServletResponse}
037 * <li>{@link OutputStream}
038 * <li>{@link Writer}
039 * </ul>
040 *
041 * @author Arjen Poutsma
042 * @author Rossen Stoyanchev
043 * @author Juergen Hoeller
044 * @since 3.1
045 */
046public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver {
047
048        @Override
049        public boolean supportsParameter(MethodParameter parameter) {
050                Class<?> paramType = parameter.getParameterType();
051                return (ServletResponse.class.isAssignableFrom(paramType) ||
052                                OutputStream.class.isAssignableFrom(paramType) ||
053                                Writer.class.isAssignableFrom(paramType));
054        }
055
056        /**
057         * Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
058         * {@code false} to indicate that the method signature provides access
059         * to the response. If subsequently the underlying method returns
060         * {@code null}, the request is considered directly handled.
061         */
062        @Override
063        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
064                        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
065
066                if (mavContainer != null) {
067                        mavContainer.setRequestHandled(true);
068                }
069
070                Class<?> paramType = parameter.getParameterType();
071
072                // ServletResponse, HttpServletResponse
073                if (ServletResponse.class.isAssignableFrom(paramType)) {
074                        return resolveNativeResponse(webRequest, paramType);
075                }
076
077                // ServletResponse required for all further argument types
078                return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class));
079        }
080
081        private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
082                T nativeResponse = webRequest.getNativeResponse(requiredType);
083                if (nativeResponse == null) {
084                        throw new IllegalStateException(
085                                        "Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
086                }
087                return nativeResponse;
088        }
089
090        private Object resolveArgument(Class<?> paramType, ServletResponse response) throws IOException {
091                if (OutputStream.class.isAssignableFrom(paramType)) {
092                        return response.getOutputStream();
093                }
094                else if (Writer.class.isAssignableFrom(paramType)) {
095                        return response.getWriter();
096                }
097
098                // Should never happen...
099                throw new UnsupportedOperationException("Unknown parameter type: " + paramType);
100        }
101
102}