001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.protocol.http; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.Enumeration; 022 023import javax.servlet.FilterConfig; 024import javax.servlet.ServletContext; 025import javax.servlet.ServletException; 026import javax.servlet.http.HttpServlet; 027import javax.servlet.http.HttpServletRequest; 028import javax.servlet.http.HttpServletResponse; 029 030import org.apache.wicket.util.io.Streams; 031import org.apache.wicket.util.string.Strings; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035/** 036 * 037 * Please use {@link WicketFilter} if you require advanced chaining of resources. 038 * 039 * <p> 040 * Servlet class for all wicket applications. The specific application class to instantiate should 041 * be specified to the application server via an init-params argument named "applicationClassName" 042 * in the servlet declaration, which is typically in a <i>web.xml </i> file. The servlet declaration 043 * may vary from one application server to another, but should look something like this: 044 * 045 * <pre> 046 * <servlet> 047 * <servlet-name>MyApplication</servlet-name> 048 * <servlet-class>org.apache.wicket.protocol.http.WicketServlet</servlet-class> 049 * <init-param> 050 * <param-name>applicationClassName</param-name> 051 * <param-value>com.whoever.MyApplication</param-value> 052 * </init-param> 053 * <load-on-startup>1</load-on-startup> 054 * </servlet> 055 * </pre> 056 * 057 * Note that the applicationClassName parameter you specify must be the fully qualified name of a 058 * class that extends WebApplication. If your class cannot be found, does not extend WebApplication 059 * or cannot be instantiated, a runtime exception of type WicketRuntimeException will be thrown. 060 * <p> 061 * As an alternative, you can configure an application factory instead. This looks like: 062 * 063 * <pre> 064 * <init-param> 065 * <param-name>applicationFactoryClassName</param-name> 066 * <param-value>teachscape.platform.web.wicket.SpringApplicationFactory</param-value> 067 * </init-param> 068 * </pre> 069 * 070 * and it has to satisfy interface {@link org.apache.wicket.protocol.http.IWebApplicationFactory}. 071 * 072 * <p> 073 * The servlet can also be configured to skip certain paths, this is especially useful when the 074 * servlet is mapped to <code>/*</code> mapping: 075 * 076 * <pre> 077 * <init-param> 078 * <param-name>ignorePaths</param-name> 079 * <param-value>/images/products/,/documents/pdf/</param-value> 080 * </init-param> 081 * </pre> 082 * 083 * <p> 084 * When GET/POST requests are made via HTTP, a WebRequestCycle object is created from the request, 085 * response and session objects (after wrapping them in the appropriate wicket wrappers). The 086 * RequestCycle's render() method is then called to produce a response to the HTTP request. 087 * <p> 088 * If you want to use servlet specific configuration, e.g. using init parameters from the 089 * {@link javax.servlet.ServletConfig}object, you should override the init() method of 090 * {@link javax.servlet.GenericServlet}. For example: 091 * 092 * <pre> 093 * public void init() throws ServletException 094 * { 095 * ServletConfig config = getServletConfig(); 096 * String webXMLParameter = config.getInitParameter("myWebXMLParameter"); 097 * ... 098 * </pre> 099 * 100 * <p> 101 * In order to support frameworks like Spring, the class is non-final and the variable 102 * webApplication is protected instead of private. Thus subclasses may provide their own means of 103 * providing the application object. 104 * 105 * @see org.apache.wicket.request.cycle.RequestCycle 106 * @author Jonathan Locke 107 * @author Timur Mehrvarz 108 * @author Juergen Donnerstag 109 * @author Igor Vaynberg (ivaynberg) 110 * @author Al Maw 111 */ 112public class WicketServlet extends HttpServlet 113{ 114 private static final long serialVersionUID = 1L; 115 116 /** Log. */ 117 private static final Logger log = LoggerFactory.getLogger(WicketServlet.class); 118 119 /** The WicketFilter where all the handling is done */ 120 protected transient WicketFilter wicketFilter; 121 122 /** 123 * Handles servlet page requests. 124 * 125 * @param servletRequest 126 * Servlet request object 127 * @param servletResponse 128 * Servlet response object 129 * @throws ServletException 130 * Thrown if something goes wrong during request handling 131 * @throws IOException 132 */ 133 @Override 134 public final void doGet(final HttpServletRequest servletRequest, 135 final HttpServletResponse servletResponse) throws ServletException, IOException 136 { 137 if (wicketFilter.processRequest(servletRequest, servletResponse, null) == false) 138 { 139 fallback(servletRequest, servletResponse); 140 } 141 } 142 143 /** 144 * Calls doGet with arguments. 145 * 146 * @param servletRequest 147 * Servlet request object 148 * @param servletResponse 149 * Servlet response object 150 * @see WicketServlet#doGet(HttpServletRequest, HttpServletResponse) 151 * @throws ServletException 152 * Thrown if something goes wrong during request handling 153 * @throws IOException 154 */ 155 @Override 156 public final void doPost(final HttpServletRequest servletRequest, 157 final HttpServletResponse servletResponse) throws ServletException, IOException 158 { 159 if (wicketFilter.processRequest(servletRequest, servletResponse, null) == false) 160 { 161 fallback(servletRequest, servletResponse); 162 } 163 } 164 165 /** 166 * 167 * @param httpServletRequest 168 * @return URL 169 */ 170 private static String getURL(final HttpServletRequest httpServletRequest) 171 { 172 /* 173 * Servlet 2.3 specification : 174 * 175 * Servlet Path: The path section that directly corresponds to the mapping which activated 176 * this request. This path starts with a "/" character except in the case where the request 177 * is matched with the "/*" pattern, in which case it is the empty string. 178 * 179 * PathInfo: The part of the request path that is not part of the Context Path or the 180 * Servlet Path. It is either null if there is no extra path, or is a string with a leading 181 * "/". 182 */ 183 String url = httpServletRequest.getServletPath(); 184 final String pathInfo = httpServletRequest.getPathInfo(); 185 186 if (pathInfo != null) 187 { 188 url += pathInfo; 189 } 190 191 String queryString = httpServletRequest.getQueryString(); 192 if (queryString != null) 193 { 194 url += ("?" + queryString); 195 } 196 197 // If url is non-empty it will start with '/', which we should lose 198 if (url.length() > 0 && url.charAt(0) == '/') 199 { 200 // Remove leading '/' 201 url = url.substring(1); 202 } 203 return url; 204 } 205 206 /** 207 * 208 * @param request 209 * @param response 210 * @throws IOException 211 */ 212 private void fallback(final HttpServletRequest request, final HttpServletResponse response) 213 throws IOException 214 { 215 // The ServletWebRequest is created here to avoid code duplication. The getURL call doesn't 216 // depend on anything wicket specific 217 String url = getURL(request); 218 219 // WICKET-2185: strip of query string 220 if (url.indexOf('?') != -1) 221 { 222 url = Strings.beforeFirst(url, '?'); 223 } 224 225 // Get the relative URL we need for loading the resource from the servlet context 226 // NOTE: we NEED to put the '/' in front as otherwise some versions of application servers 227 // (e.g. Jetty 5.1.x) will fail for requests like '/mysubdir/myfile.css' 228 if ((url.length() > 0 && url.charAt(0) != '/') || url.length() == 0) 229 { 230 url = '/' + url; 231 } 232 233 InputStream stream = getServletContext().getResourceAsStream(url); 234 String mimeType = getServletContext().getMimeType(url); 235 236 if (stream == null) 237 { 238 if (response.isCommitted()) 239 { 240 response.setStatus(HttpServletResponse.SC_NOT_FOUND); 241 } 242 else 243 { 244 response.sendError(HttpServletResponse.SC_NOT_FOUND); 245 } 246 } 247 else 248 { 249 if (mimeType != null) 250 { 251 response.setContentType(mimeType); 252 } 253 try 254 { 255 Streams.copy(stream, response.getOutputStream()); 256 } 257 finally 258 { 259 stream.close(); 260 } 261 } 262 } 263 264 /** 265 * Servlet initialization 266 */ 267 @Override 268 public void init() throws ServletException 269 { 270 wicketFilter = newWicketFilter(); 271 wicketFilter.init(true, new FilterConfig() 272 { 273 /** 274 * @see javax.servlet.FilterConfig#getServletContext() 275 */ 276 @Override 277 public ServletContext getServletContext() 278 { 279 return WicketServlet.this.getServletContext(); 280 } 281 282 /** 283 * @see javax.servlet.FilterConfig#getInitParameterNames() 284 */ 285 @Override 286 @SuppressWarnings("unchecked") 287 public Enumeration<String> getInitParameterNames() 288 { 289 return WicketServlet.this.getInitParameterNames(); 290 } 291 292 /** 293 * @see javax.servlet.FilterConfig#getInitParameter(java.lang.String) 294 */ 295 @Override 296 public String getInitParameter(final String name) 297 { 298 return WicketServlet.this.getInitParameter(name); 299 } 300 301 /** 302 * @see javax.servlet.FilterConfig#getFilterName() 303 */ 304 @Override 305 public String getFilterName() 306 { 307 return WicketServlet.this.getServletName(); 308 } 309 }); 310 } 311 312 /** 313 * @return The wicket filter 314 */ 315 protected WicketFilter newWicketFilter() 316 { 317 return new WicketFilter(); 318 } 319 320 /** 321 * Servlet cleanup. 322 */ 323 @Override 324 public void destroy() 325 { 326 wicketFilter.destroy(); 327 wicketFilter = null; 328 } 329}