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.mock.web; 018 019import java.io.IOException; 020import java.io.UnsupportedEncodingException; 021import java.util.Collections; 022import java.util.Enumeration; 023import java.util.LinkedHashMap; 024import java.util.LinkedHashSet; 025import java.util.Map; 026 027import javax.el.ELContext; 028import javax.servlet.Servlet; 029import javax.servlet.ServletConfig; 030import javax.servlet.ServletContext; 031import javax.servlet.ServletException; 032import javax.servlet.ServletRequest; 033import javax.servlet.ServletResponse; 034import javax.servlet.http.HttpServletRequest; 035import javax.servlet.http.HttpServletResponse; 036import javax.servlet.http.HttpSession; 037import javax.servlet.jsp.JspWriter; 038import javax.servlet.jsp.PageContext; 039 040import org.springframework.lang.Nullable; 041import org.springframework.util.Assert; 042 043/** 044 * Mock implementation of the {@link javax.servlet.jsp.PageContext} interface. 045 * Only necessary for testing applications when testing custom JSP tags. 046 * 047 * <p>Note: Expects initialization via the constructor rather than via the 048 * {@code PageContext.initialize} method. Does not support writing to a 049 * JspWriter, request dispatching, or {@code handlePageException} calls. 050 * 051 * @author Juergen Hoeller 052 * @since 1.0.2 053 */ 054public class MockPageContext extends PageContext { 055 056 private final ServletContext servletContext; 057 058 private final HttpServletRequest request; 059 060 private final HttpServletResponse response; 061 062 private final ServletConfig servletConfig; 063 064 private final Map<String, Object> attributes = new LinkedHashMap<>(); 065 066 @Nullable 067 private JspWriter out; 068 069 070 /** 071 * Create new MockPageContext with a default {@link MockServletContext}, 072 * {@link MockHttpServletRequest}, {@link MockHttpServletResponse}, 073 * {@link MockServletConfig}. 074 */ 075 public MockPageContext() { 076 this(null, null, null, null); 077 } 078 079 /** 080 * Create new MockPageContext with a default {@link MockHttpServletRequest}, 081 * {@link MockHttpServletResponse}, {@link MockServletConfig}. 082 * @param servletContext the ServletContext that the JSP page runs in 083 * (only necessary when actually accessing the ServletContext) 084 */ 085 public MockPageContext(@Nullable ServletContext servletContext) { 086 this(servletContext, null, null, null); 087 } 088 089 /** 090 * Create new MockPageContext with a MockHttpServletResponse, 091 * MockServletConfig. 092 * @param servletContext the ServletContext that the JSP page runs in 093 * @param request the current HttpServletRequest 094 * (only necessary when actually accessing the request) 095 */ 096 public MockPageContext(@Nullable ServletContext servletContext, @Nullable HttpServletRequest request) { 097 this(servletContext, request, null, null); 098 } 099 100 /** 101 * Create new MockPageContext with a MockServletConfig. 102 * @param servletContext the ServletContext that the JSP page runs in 103 * @param request the current HttpServletRequest 104 * @param response the current HttpServletResponse 105 * (only necessary when actually writing to the response) 106 */ 107 public MockPageContext(@Nullable ServletContext servletContext, @Nullable HttpServletRequest request, 108 @Nullable HttpServletResponse response) { 109 110 this(servletContext, request, response, null); 111 } 112 113 /** 114 * Create new MockServletConfig. 115 * @param servletContext the ServletContext that the JSP page runs in 116 * @param request the current HttpServletRequest 117 * @param response the current HttpServletResponse 118 * @param servletConfig the ServletConfig (hardly ever accessed from within a tag) 119 */ 120 public MockPageContext(@Nullable ServletContext servletContext, @Nullable HttpServletRequest request, 121 @Nullable HttpServletResponse response, @Nullable ServletConfig servletConfig) { 122 123 this.servletContext = (servletContext != null ? servletContext : new MockServletContext()); 124 this.request = (request != null ? request : new MockHttpServletRequest(servletContext)); 125 this.response = (response != null ? response : new MockHttpServletResponse()); 126 this.servletConfig = (servletConfig != null ? servletConfig : new MockServletConfig(servletContext)); 127 } 128 129 130 @Override 131 public void initialize( 132 Servlet servlet, ServletRequest request, ServletResponse response, 133 String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush) { 134 135 throw new UnsupportedOperationException("Use appropriate constructor"); 136 } 137 138 @Override 139 public void release() { 140 } 141 142 @Override 143 public void setAttribute(String name, @Nullable Object value) { 144 Assert.notNull(name, "Attribute name must not be null"); 145 if (value != null) { 146 this.attributes.put(name, value); 147 } 148 else { 149 this.attributes.remove(name); 150 } 151 } 152 153 @Override 154 public void setAttribute(String name, @Nullable Object value, int scope) { 155 Assert.notNull(name, "Attribute name must not be null"); 156 switch (scope) { 157 case PAGE_SCOPE: 158 setAttribute(name, value); 159 break; 160 case REQUEST_SCOPE: 161 this.request.setAttribute(name, value); 162 break; 163 case SESSION_SCOPE: 164 this.request.getSession().setAttribute(name, value); 165 break; 166 case APPLICATION_SCOPE: 167 this.servletContext.setAttribute(name, value); 168 break; 169 default: 170 throw new IllegalArgumentException("Invalid scope: " + scope); 171 } 172 } 173 174 @Override 175 @Nullable 176 public Object getAttribute(String name) { 177 Assert.notNull(name, "Attribute name must not be null"); 178 return this.attributes.get(name); 179 } 180 181 @Override 182 @Nullable 183 public Object getAttribute(String name, int scope) { 184 Assert.notNull(name, "Attribute name must not be null"); 185 switch (scope) { 186 case PAGE_SCOPE: 187 return getAttribute(name); 188 case REQUEST_SCOPE: 189 return this.request.getAttribute(name); 190 case SESSION_SCOPE: 191 HttpSession session = this.request.getSession(false); 192 return (session != null ? session.getAttribute(name) : null); 193 case APPLICATION_SCOPE: 194 return this.servletContext.getAttribute(name); 195 default: 196 throw new IllegalArgumentException("Invalid scope: " + scope); 197 } 198 } 199 200 @Override 201 @Nullable 202 public Object findAttribute(String name) { 203 Object value = getAttribute(name); 204 if (value == null) { 205 value = getAttribute(name, REQUEST_SCOPE); 206 if (value == null) { 207 value = getAttribute(name, SESSION_SCOPE); 208 if (value == null) { 209 value = getAttribute(name, APPLICATION_SCOPE); 210 } 211 } 212 } 213 return value; 214 } 215 216 @Override 217 public void removeAttribute(String name) { 218 Assert.notNull(name, "Attribute name must not be null"); 219 this.removeAttribute(name, PageContext.PAGE_SCOPE); 220 this.removeAttribute(name, PageContext.REQUEST_SCOPE); 221 this.removeAttribute(name, PageContext.SESSION_SCOPE); 222 this.removeAttribute(name, PageContext.APPLICATION_SCOPE); 223 } 224 225 @Override 226 public void removeAttribute(String name, int scope) { 227 Assert.notNull(name, "Attribute name must not be null"); 228 switch (scope) { 229 case PAGE_SCOPE: 230 this.attributes.remove(name); 231 break; 232 case REQUEST_SCOPE: 233 this.request.removeAttribute(name); 234 break; 235 case SESSION_SCOPE: 236 this.request.getSession().removeAttribute(name); 237 break; 238 case APPLICATION_SCOPE: 239 this.servletContext.removeAttribute(name); 240 break; 241 default: 242 throw new IllegalArgumentException("Invalid scope: " + scope); 243 } 244 } 245 246 @Override 247 public int getAttributesScope(String name) { 248 if (getAttribute(name) != null) { 249 return PAGE_SCOPE; 250 } 251 else if (getAttribute(name, REQUEST_SCOPE) != null) { 252 return REQUEST_SCOPE; 253 } 254 else if (getAttribute(name, SESSION_SCOPE) != null) { 255 return SESSION_SCOPE; 256 } 257 else if (getAttribute(name, APPLICATION_SCOPE) != null) { 258 return APPLICATION_SCOPE; 259 } 260 else { 261 return 0; 262 } 263 } 264 265 public Enumeration<String> getAttributeNames() { 266 return Collections.enumeration(new LinkedHashSet<>(this.attributes.keySet())); 267 } 268 269 @Override 270 public Enumeration<String> getAttributeNamesInScope(int scope) { 271 switch (scope) { 272 case PAGE_SCOPE: 273 return getAttributeNames(); 274 case REQUEST_SCOPE: 275 return this.request.getAttributeNames(); 276 case SESSION_SCOPE: 277 HttpSession session = this.request.getSession(false); 278 return (session != null ? session.getAttributeNames() : Collections.emptyEnumeration()); 279 case APPLICATION_SCOPE: 280 return this.servletContext.getAttributeNames(); 281 default: 282 throw new IllegalArgumentException("Invalid scope: " + scope); 283 } 284 } 285 286 @Override 287 public JspWriter getOut() { 288 if (this.out == null) { 289 this.out = new MockJspWriter(this.response); 290 } 291 return this.out; 292 } 293 294 @Override 295 @Deprecated 296 public javax.servlet.jsp.el.ExpressionEvaluator getExpressionEvaluator() { 297 return new MockExpressionEvaluator(this); 298 } 299 300 @Override 301 @Nullable 302 public ELContext getELContext() { 303 return null; 304 } 305 306 @Override 307 @Deprecated 308 @Nullable 309 public javax.servlet.jsp.el.VariableResolver getVariableResolver() { 310 return null; 311 } 312 313 @Override 314 public HttpSession getSession() { 315 return this.request.getSession(); 316 } 317 318 @Override 319 public Object getPage() { 320 return this; 321 } 322 323 @Override 324 public ServletRequest getRequest() { 325 return this.request; 326 } 327 328 @Override 329 public ServletResponse getResponse() { 330 return this.response; 331 } 332 333 @Override 334 @Nullable 335 public Exception getException() { 336 return null; 337 } 338 339 @Override 340 public ServletConfig getServletConfig() { 341 return this.servletConfig; 342 } 343 344 @Override 345 public ServletContext getServletContext() { 346 return this.servletContext; 347 } 348 349 @Override 350 public void forward(String path) throws ServletException, IOException { 351 this.request.getRequestDispatcher(path).forward(this.request, this.response); 352 } 353 354 @Override 355 public void include(String path) throws ServletException, IOException { 356 this.request.getRequestDispatcher(path).include(this.request, this.response); 357 } 358 359 @Override 360 public void include(String path, boolean flush) throws ServletException, IOException { 361 this.request.getRequestDispatcher(path).include(this.request, this.response); 362 if (flush) { 363 this.response.flushBuffer(); 364 } 365 } 366 367 public byte[] getContentAsByteArray() { 368 Assert.state(this.response instanceof MockHttpServletResponse, "MockHttpServletResponse required"); 369 return ((MockHttpServletResponse) this.response).getContentAsByteArray(); 370 } 371 372 public String getContentAsString() throws UnsupportedEncodingException { 373 Assert.state(this.response instanceof MockHttpServletResponse, "MockHttpServletResponse required"); 374 return ((MockHttpServletResponse) this.response).getContentAsString(); 375 } 376 377 @Override 378 public void handlePageException(Exception ex) throws ServletException, IOException { 379 throw new ServletException("Page exception", ex); 380 } 381 382 @Override 383 public void handlePageException(Throwable ex) throws ServletException, IOException { 384 throw new ServletException("Page exception", ex); 385 } 386 387}