001/* 002 * Copyright 2002-2016 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; 018 019import javax.servlet.http.HttpServletRequest; 020import javax.servlet.http.HttpServletResponse; 021import javax.servlet.http.HttpSession; 022 023import org.springframework.http.HttpMethod; 024import org.springframework.web.servlet.ModelAndView; 025import org.springframework.web.servlet.support.WebContentGenerator; 026import org.springframework.web.util.WebUtils; 027 028/** 029 * Convenient superclass for controller implementations, using the Template Method 030 * design pattern. 031 * 032 * <p><b><a name="workflow">Workflow 033 * (<a href="Controller.html#workflow">and that defined by interface</a>):</b><br> 034 * <ol> 035 * <li>{@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest()} 036 * will be called by the DispatcherServlet</li> 037 * <li>Inspection of supported methods (ServletException if request method 038 * is not support)</li> 039 * <li>If session is required, try to get it (ServletException if not found)</li> 040 * <li>Set caching headers if needed according to the cacheSeconds property</li> 041 * <li>Call abstract method {@link #handleRequestInternal(HttpServletRequest, HttpServletResponse) handleRequestInternal()} 042 * (optionally synchronizing around the call on the HttpSession), 043 * which should be implemented by extending classes to provide actual 044 * functionality to return {@link org.springframework.web.servlet.ModelAndView ModelAndView} objects.</li> 045 * </ol> 046 * 047 * <p><b><a name="config">Exposed configuration properties</a> 048 * (<a href="Controller.html#config">and those defined by interface</a>):</b><br> 049 * <table border="1"> 050 * <tr> 051 * <td><b>name</b></th> 052 * <td><b>default</b></td> 053 * <td><b>description</b></td> 054 * </tr> 055 * <tr> 056 * <td>supportedMethods</td> 057 * <td>GET,POST</td> 058 * <td>comma-separated (CSV) list of methods supported by this controller, 059 * such as GET, POST and PUT</td> 060 * </tr> 061 * <tr> 062 * <td>requireSession</td> 063 * <td>false</td> 064 * <td>whether a session should be required for requests to be able to 065 * be handled by this controller. This ensures that derived controller 066 * can - without fear of null pointers - call request.getSession() to 067 * retrieve a session. If no session can be found while processing 068 * the request, a ServletException will be thrown</td> 069 * </tr> 070 * <tr> 071 * <td>cacheSeconds</td> 072 * <td>-1</td> 073 * <td>indicates the amount of seconds to include in the cache header 074 * for the response following on this request. 0 (zero) will include 075 * headers for no caching at all, -1 (the default) will not generate 076 * <i>any headers</i> and any positive number will generate headers 077 * that state the amount indicated as seconds to cache the content</td> 078 * </tr> 079 * <tr> 080 * <td>synchronizeOnSession</td> 081 * <td>false</td> 082 * <td>whether the call to {@code handleRequestInternal} should be 083 * synchronized around the HttpSession, to serialize invocations 084 * from the same client. No effect if there is no HttpSession. 085 * </td> 086 * </tr> 087 * </table> 088 * 089 * @author Rod Johnson 090 * @author Juergen Hoeller 091 * @author Rossen Stoyanchev 092 * @see WebContentInterceptor 093 */ 094public abstract class AbstractController extends WebContentGenerator implements Controller { 095 096 private boolean synchronizeOnSession = false; 097 098 099 /** 100 * Create a new AbstractController which supports 101 * HTTP methods GET, HEAD and POST by default. 102 */ 103 public AbstractController() { 104 this(true); 105 } 106 107 /** 108 * Create a new AbstractController. 109 * @param restrictDefaultSupportedMethods {@code true} if this 110 * controller should support HTTP methods GET, HEAD and POST by default, 111 * or {@code false} if it should be unrestricted 112 * @since 4.3 113 */ 114 public AbstractController(boolean restrictDefaultSupportedMethods) { 115 super(restrictDefaultSupportedMethods); 116 } 117 118 119 /** 120 * Set if controller execution should be synchronized on the session, 121 * to serialize parallel invocations from the same client. 122 * <p>More specifically, the execution of the {@code handleRequestInternal} 123 * method will get synchronized if this flag is "true". The best available 124 * session mutex will be used for the synchronization; ideally, this will 125 * be a mutex exposed by HttpSessionMutexListener. 126 * <p>The session mutex is guaranteed to be the same object during 127 * the entire lifetime of the session, available under the key defined 128 * by the {@code SESSION_MUTEX_ATTRIBUTE} constant. It serves as a 129 * safe reference to synchronize on for locking on the current session. 130 * <p>In many cases, the HttpSession reference itself is a safe mutex 131 * as well, since it will always be the same object reference for the 132 * same active logical session. However, this is not guaranteed across 133 * different servlet containers; the only 100% safe way is a session mutex. 134 * @see AbstractController#handleRequestInternal 135 * @see org.springframework.web.util.HttpSessionMutexListener 136 * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) 137 */ 138 public final void setSynchronizeOnSession(boolean synchronizeOnSession) { 139 this.synchronizeOnSession = synchronizeOnSession; 140 } 141 142 /** 143 * Return whether controller execution should be synchronized on the session. 144 */ 145 public final boolean isSynchronizeOnSession() { 146 return this.synchronizeOnSession; 147 } 148 149 150 @Override 151 public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) 152 throws Exception { 153 154 if (HttpMethod.OPTIONS.matches(request.getMethod())) { 155 response.setHeader("Allow", getAllowHeader()); 156 return null; 157 } 158 159 // Delegate to WebContentGenerator for checking and preparing. 160 checkRequest(request); 161 prepareResponse(response); 162 163 // Execute handleRequestInternal in synchronized block if required. 164 if (this.synchronizeOnSession) { 165 HttpSession session = request.getSession(false); 166 if (session != null) { 167 Object mutex = WebUtils.getSessionMutex(session); 168 synchronized (mutex) { 169 return handleRequestInternal(request, response); 170 } 171 } 172 } 173 174 return handleRequestInternal(request, response); 175 } 176 177 /** 178 * Template method. Subclasses must implement this. 179 * The contract is the same as for {@code handleRequest}. 180 * @see #handleRequest 181 */ 182 protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 183 throws Exception; 184 185}