All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.webapp.FacesServlet Maven / Gradle / Ivy

Go to download

Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 4.1.0
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package javax.faces.webapp;

import static java.util.Collections.emptySet;
import static java.util.EnumSet.allOf;
import static java.util.EnumSet.range;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINER;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
import static javax.faces.FactoryFinder.FACES_CONTEXT_FACTORY;
import static javax.faces.FactoryFinder.LIFECYCLE_FACTORY;
import static javax.faces.lifecycle.LifecycleFactory.DEFAULT_LIFECYCLE;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;

import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ResourceHandler;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 

* FacesServlet is a servlet that manages the * request processing lifecycle for web applications that are utilizing JavaServer Faces to * construct the user interface. *

* *
* *

* If the application is running in a Servlet 3.0 (and beyond) container, the runtime must provide * an implementation of the {@link javax.servlet.ServletContainerInitializer} interface that * declares the following classes in its {@link javax.servlet.annotation.HandlesTypes} annotation. *

* *
    * *
  • {@link javax.faces.annotation.FacesConfig}
  • * *
  • {@link javax.faces.application.ResourceDependencies}
  • * *
  • {@link javax.faces.application.ResourceDependency}
  • * *
  • javax.faces.bean.ManagedBean
  • * *
  • {@link javax.faces.component.FacesComponent}
  • * *
  • {@link javax.faces.component.UIComponent}
  • * *
  • {@link javax.faces.convert.Converter}
  • * *
  • {@link javax.faces.convert.FacesConverter}
  • * *
  • {@link javax.faces.event.ListenerFor}
  • * *
  • {@link javax.faces.event.ListenersFor}
  • * *
  • {@link javax.faces.render.FacesBehaviorRenderer}
  • * *
  • {@link javax.faces.render.Renderer}
  • * *
  • {@link javax.faces.validator.FacesValidator}
  • * *
  • {@link javax.faces.validator.Validator}
  • * *
* *

* This servlet must automatically be mapped if it is not explicitly mapped in * web.xml or web-fragment.xml and one or more of the following conditions * are true. *

* *
    * *
  • *

    * A faces-config.xml file is found in WEB-INF *

    *
  • * *
  • *

    * A faces-config.xml file is found in the META-INF directory of a jar in * the application's classpath. *

    *
  • * *
  • *

    * A filename ending in .faces-config.xml is found in the META-INF * directory of a jar in the application's classpath. *

    *
  • * *
  • *

    * The javax.faces.CONFIG_FILES context param is declared in web.xml or * web-fragment.xml. *

    *
  • * *
  • *

    * The Set of classes passed to the onStartup() method of the * ServletContainerInitializer implementation is not empty. *

    *
  • * *
* *

* If the runtime determines that the servlet must be automatically mapped, it must be mapped to the * following <url-pattern> entries. *

* *
    *
  • /faces/*
  • *
  • *.jsf
  • *
  • *.faces
  • *
  • *.xhtml
  • *
* *
* *

* Note that the automatic mapping to {@code *.xhtml} can be disabled with the context param * {@link #DISABLE_FACESSERVLET_TO_XHTML_PARAM_NAME}. *

* *
* *

* This class must be annotated with {@code javax.servlet.annotation.MultipartConfig}. This causes * the Servlet container in which the JSF implementation is running to correctly handle multipart * form data. *

* *

* Some security considerations relating to this class *

* *

* The topic of web application security is a cross-cutting concern and every aspect of the * specification address it. However, as with any framework, the application developer needs to pay * careful attention to security. Please consider these topics among the rest of the security * concerns for the application. This is by no means a complete list of security concerns, and is no * substitute for a thorough application level security review. *

* *
* *

* Prefix mappings and the FacesServlet *

* *

* If the FacesServlet is mapped using a prefix <url-pattern>, such * as <url-pattern>/faces/*</url-pattern>, something must be done to * prevent access to the view source without its first being processed by the * FacesServlet. One common approach is to apply a <security-constraint> to all * facelet files and flow definition files. Please see the Deployment Descriptor * chapter of the Java Servlet Specification for more information the use of * <security-constraint>. *

* *

* Allowable HTTP Methods *

* *

* The JSF specification only requires the use of the GET and POST http methods. If your web * application does not require any other http methods, such as PUT and DELETE, please consider * restricting the allowable http methods using the <http-method> and * <http-method-omission> elements. Please see the Security of the Java * Servlet Specification for more information the use of these elements. *

* * *
* *
*/ @MultipartConfig public final class FacesServlet implements Servlet { /** *

* Context initialization parameter name for a comma delimited list of context-relative resource * paths (in addition to /WEB-INF/faces-config.xml which is loaded automatically if * it exists) containing JavaServer Faces configuration information. *

*/ public static final String CONFIG_FILES_ATTR = "javax.faces.CONFIG_FILES"; /** *

* Context initialization parameter name for the lifecycle identifier of the {@link Lifecycle} * instance to be utilized. *

*/ public static final String LIFECYCLE_ID_ATTR = "javax.faces.LIFECYCLE_ID"; /** *

* The ServletContext init parameter consulted by the runtime to tell if the * automatic mapping of the {@code FacesServlet} to the extension {@code *.xhtml} should be * disabled. The implementation must disable this automatic mapping if and only if the value of * this parameter is equal, ignoring case, to {@code true}. *

* *

* If this parameter is not specified, this automatic mapping is enabled as specified above. *

*/ public static final String DISABLE_FACESSERVLET_TO_XHTML_PARAM_NAME = "javax.faces.DISABLE_FACESSERVLET_TO_XHTML"; /** * The Logger for this class. */ private static final Logger LOGGER = Logger.getLogger("javax.faces.webapp", "javax.faces.LogStrings"); /** * A white space separated list of case sensitive HTTP method names that are allowed to be * processed by this servlet. * means allow all */ private static final String ALLOWED_HTTP_METHODS_ATTR = "com.sun.faces.allowedHttpMethods"; // Http method names must be upper case. http://www.w3.org/Protocols/HTTP/NoteMethodCS.html // List of valid methods in Http 1.1 http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9 private enum HttpMethod { OPTIONS("OPTIONS"), GET("GET"), HEAD("HEAD"), POST("POST"), PUT("PUT"), DELETE("DELETE"), TRACE("TRACE"), CONNECT("CONNECT"); private String name; HttpMethod(String name) { this.name = name; } @Override public String toString() { return name; } } private final Set defaultAllowedHttpMethods = range(HttpMethod.OPTIONS, HttpMethod.CONNECT); private Set allowedUnknownHttpMethods; private Set allowedKnownHttpMethods; private Set allHttpMethods; private boolean allowAllMethods; /** *

* Factory for {@link FacesContext} instances. *

*/ private FacesContextFactory facesContextFactory; /** *

* The {@link Lifecycle} instance to use for request processing. *

*/ private Lifecycle lifecycle; /** *

* The ServletConfig instance for this servlet. *

*/ private ServletConfig servletConfig; /** * From GLASSFISH-15632. If true, the FacesContext instance left over from startup time has been * released. */ private boolean initFacesContextReleased; /** *

* Acquire the factory instances we will require. *

* * @throws ServletException if, for any reason, the startup of this Faces application failed. * This includes errors in the config file that is parsed before or during the * processing of this init() method. */ @Override public void init(ServletConfig servletConfig) throws ServletException { // Save our ServletConfig instance this.servletConfig = servletConfig; // Acquire our FacesContextFactory instance facesContextFactory = acquireFacesContextFactory(); // Acquire our Lifecycle instance lifecycle = acquireLifecycle(); initHttpMethodValidityVerificationWithCatch(); } /** *

* Process an incoming request, and create the * corresponding response according to the following specification. *

* *
* *

* If the request and response arguments to this method are not * instances of HttpServletRequest and HttpServletResponse, * respectively, the results of invoking this method are undefined. *

* *

* This method must respond to requests that contain * the following strings by invoking the sendError method on the response argument * (cast to HttpServletResponse), passing the code * HttpServletResponse.SC_NOT_FOUND as the argument. *

* *
     * 
     * /WEB-INF/
     * /WEB-INF
     * /META-INF/
     * /META-INF
     * 
     * 
* *

* If none of the cases described above in the specification for this method apply to the * servicing of this request, the following action must be taken to service the request. *

* *

* Acquire a {@link FacesContext} instance for this request. *

* *

* Acquire the ResourceHandler for this request by calling * {@link javax.faces.application.Application#getResourceHandler}. Call * {@link javax.faces.application.ResourceHandler#isResourceRequest}. * * If this returns true call {@link javax.faces.application.ResourceHandler#handleResourceRequest}. * * If this returns false, call {@link javax.faces.lifecycle.Lifecycle#attachWindow} * followed by {@link javax.faces.lifecycle.Lifecycle#execute} followed by * {@link javax.faces.lifecycle.Lifecycle#render}. * * If a {@link javax.faces.FacesException} is thrown in either case, extract the cause from the FacesException. * * If the cause is null extract the message from the FacesException, put it inside * of a new ServletException instance, and pass the FacesException * instance as the root cause, then rethrow the ServletException instance. * If the cause is an instance of ServletException, rethrow the cause. * If the cause is an instance of IOException, rethrow the cause. * * Otherwise, create a new ServletException instance, passing the message from the cause, as the first * argument, and the cause itself as the second argument. *

* *

* The implementation must make it so {@link javax.faces.context.FacesContext#release} is called * within a finally block as late as possible in the processing for the JSF related portion of * this request. *

* *
* * @param req The servlet request we are processing * @param resp The servlet response we are creating * * @throws IOException if an input/output error occurs during processing * @throws ServletException if a servlet error occurs during processing * */ @Override public void service(ServletRequest req, ServletResponse resp) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; requestStart(request.getRequestURI()); // V3 Probe hook if (!isHttpMethodValid(request)) { response.sendError(SC_BAD_REQUEST); return; } logIfThreadInterruped(); // If prefix mapped, then ensure requests for /WEB-INF are not processed. if (notProcessWebInfIfPrefixMapped(request, response)) { return; } releaseFacesInitContextIfNeeded(); // Acquire the FacesContext instance for this request FacesContext context = acquireFacesContext(request, response); // Execute the request processing lifecycle for this request try { executeLifecyle(context); } finally { // Release the FacesContext instance for this request context.release(); } requestEnd(); // V3 Probe hook } /** *

* Release all resources acquired at startup time. *

*/ @Override public void destroy() { facesContextFactory = null; lifecycle = null; servletConfig = null; uninitHttpMethodValidityVerification(); } /** *

* Return the ServletConfig instance for this servlet. *

*/ @Override public ServletConfig getServletConfig() { return servletConfig; } /** *

* Return information about this Servlet. *

*/ @Override public String getServletInfo() { return getClass().getName(); } // --------------------------------------------------------- Private Methods private FacesContextFactory acquireFacesContextFactory() throws UnavailableException { try { return (FacesContextFactory) FactoryFinder.getFactory(FACES_CONTEXT_FACTORY); } catch (FacesException e) { String msg = LOGGER.getResourceBundle().getString("severe.webapp.facesservlet.init_failed"); LOGGER.log(SEVERE, msg, e.getCause() != null ? e.getCause() : e); throw new UnavailableException(msg); } } private Lifecycle acquireLifecycle() throws ServletException { try { LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(LIFECYCLE_FACTORY); // First look in the servlet init-param set String lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR); if (lifecycleId == null) { // If not found, look in the context-param set lifecycleId = servletConfig.getServletContext().getInitParameter(LIFECYCLE_ID_ATTR); } if (lifecycleId == null) { // If still not found, use the default lifecycleId = DEFAULT_LIFECYCLE; } return lifecycleFactory.getLifecycle(lifecycleId); } catch (FacesException e) { Throwable rootCause = e.getCause(); if (rootCause == null) { throw e; } throw new ServletException(e.getMessage(), rootCause); } } private FacesContext acquireFacesContext(HttpServletRequest request, HttpServletResponse response) { return facesContextFactory.getFacesContext(servletConfig.getServletContext(), request, response, lifecycle); } private void initHttpMethodValidityVerificationWithCatch() throws ServletException { try { initHttpMethodValidityVerification(); } catch (FacesException e) { Throwable rootCause = e.getCause(); if (rootCause == null) { throw e; } else { throw new ServletException(e.getMessage(), rootCause); } } } private void initHttpMethodValidityVerification() { allHttpMethods = allOf(HttpMethod.class); // Configure our permitted HTTP methods allowedUnknownHttpMethods = emptySet(); allowedKnownHttpMethods = defaultAllowedHttpMethods; String allowedHttpMethodsString = servletConfig.getServletContext().getInitParameter(ALLOWED_HTTP_METHODS_ATTR); if (allowedHttpMethodsString != null) { String[] methods = allowedHttpMethodsString.split("\\s+"); allowedUnknownHttpMethods = new HashSet<>(methods.length); List allowedKnownHttpMethodsStringList = new ArrayList<>(); // Validate input against allHttpMethods data structure for (String httpMethod : methods) { if (httpMethod.equals("*")) { allowAllMethods = true; allowedUnknownHttpMethods = emptySet(); return; } if (!isKnownHttpMethod(httpMethod)) { logUnknownHttpMethod(httpMethod); // prevent duplicates if (!allowedUnknownHttpMethods.contains(httpMethod)) { allowedUnknownHttpMethods.add(httpMethod); } } else { // prevent duplicates if (!allowedKnownHttpMethodsStringList.contains(httpMethod)) { allowedKnownHttpMethodsStringList.add(httpMethod); } } } // Optimally initialize allowedKnownHttpMethods initializeAllowedKnownHttpMethods(allowedKnownHttpMethodsStringList); } } private void initializeAllowedKnownHttpMethods(List allowedKnownHttpMethodsStringList) { if (5 == allowedKnownHttpMethodsStringList.size()) { allowedKnownHttpMethods = EnumSet.of( HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(0)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(1)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(2)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(3)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(4))); } else if (4 == allowedKnownHttpMethodsStringList.size()) { allowedKnownHttpMethods = EnumSet.of( HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(0)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(1)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(2)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(3))); } else if (3 == allowedKnownHttpMethodsStringList.size()) { allowedKnownHttpMethods = EnumSet.of( HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(0)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(1)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(2))); } else if (2 == allowedKnownHttpMethodsStringList.size()) { allowedKnownHttpMethods = EnumSet.of( HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(0)), HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(1))); } else if (1 == allowedKnownHttpMethodsStringList.size()) { allowedKnownHttpMethods = EnumSet.of(HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(0))); } else { List restList = new ArrayList<>(allowedKnownHttpMethodsStringList.size() - 1); for (int i = 1; i < allowedKnownHttpMethodsStringList.size() - 1; i++) { restList.add(HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(i))); } HttpMethod first = HttpMethod.valueOf(allowedKnownHttpMethodsStringList.get(0)); HttpMethod[] rest = new HttpMethod[restList.size()]; restList.toArray(rest); allowedKnownHttpMethods = EnumSet.of(first, rest); } } private void logIfThreadInterruped() { if (Thread.currentThread().isInterrupted()) { if (LOGGER.isLoggable(FINER)) { LOGGER.log(FINE, "Thread {0} given to FacesServlet.service() in interrupted state", Thread.currentThread().getName()); } } } private void logUnknownHttpMethod(String httpMethod) { if (LOGGER.isLoggable(WARNING)) { HttpMethod[] values = HttpMethod.values(); Object[] arg = new Object[values.length + 1]; arg[0] = httpMethod; System.arraycopy(values, HttpMethod.OPTIONS.ordinal(), arg, 1, values.length); LOGGER.log(WARNING, "warning.webapp.facesservlet.init_invalid_http_method", arg); } } private boolean isKnownHttpMethod(String httpMethod) { try { HttpMethod.valueOf(httpMethod); return true; } catch (IllegalArgumentException e) { return false; } } private void releaseFacesInitContextIfNeeded() { if (!initFacesContextReleased) { FacesContext initFacesContext = FacesContext.getCurrentInstance(); if (initFacesContext != null) { initFacesContext.release(); } // Bug 20458755: ensure the special factory is removed, so as not // to incur an additional performance penalty at request processing // time. FactoryFinder.getFactory("com.sun.faces.ServletContextFacesContextFactory_Removal"); initFacesContextReleased = true; } } private boolean notProcessWebInfIfPrefixMapped(HttpServletRequest request, HttpServletResponse response) throws IOException { String pathInfo = request.getPathInfo(); if (pathInfo != null) { pathInfo = pathInfo.toUpperCase(); if (pathInfo.contains("/WEB-INF/") || pathInfo.contains("/WEB-INF") || pathInfo.contains("/META-INF/") || pathInfo.contains("/META-INF")) { response.sendError(SC_NOT_FOUND); return true; } } return false; } private void executeLifecyle(FacesContext context) throws IOException, ServletException { try { ResourceHandler handler = context.getApplication().getResourceHandler(); if (handler.isResourceRequest(context)) { handler.handleResourceRequest(context); } else { lifecycle.attachWindow(context); lifecycle.execute(context); lifecycle.render(context); } } catch (FacesException e) { Throwable t = e.getCause(); if (t == null) { throw new ServletException(e.getMessage(), e); } if (t instanceof ServletException) { throw (ServletException) t; } if (t instanceof IOException) { throw (IOException) t; } throw new ServletException(t.getMessage(), t); } } private void uninitHttpMethodValidityVerification() { assert (null != allowedUnknownHttpMethods); assert (null != defaultAllowedHttpMethods); assert (null != allHttpMethods); allowedUnknownHttpMethods.clear(); allowedUnknownHttpMethods = null; allowedKnownHttpMethods.clear(); allowedKnownHttpMethods = null; allHttpMethods.clear(); allHttpMethods = null; } private boolean isHttpMethodValid(HttpServletRequest request) { boolean result = false; if (allowAllMethods) { result = true; } else { String requestMethodString = request.getMethod(); HttpMethod requestMethod = null; boolean isKnownHttpMethod; try { requestMethod = HttpMethod.valueOf(requestMethodString); isKnownHttpMethod = true; } catch (IllegalArgumentException e) { isKnownHttpMethod = false; } if (isKnownHttpMethod) { result = allowedKnownHttpMethods.contains(requestMethod); } else { result = allowedUnknownHttpMethods.contains(requestMethodString); } } return result; } /** * DO NOT REMOVE. Necessary for V3 probe monitoring. */ private void requestStart(String requestUri) { } /** * DO NOT REMOVE. Necessary for V3 probe monitoring. */ private void requestEnd() { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy