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

jakarta.faces.application.ViewHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1997, 2020 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 jakarta.faces.application;

import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Stream;

import jakarta.faces.FacesException;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.push.PushContext;
import jakarta.faces.view.ViewDeclarationLanguage;

/**
 * 

* * ViewHandler is the pluggablity mechanism for allowing implementations of or applications using the * Jakarta Faces Specification to provide their own handling of the activities in the Render Response * and Restore View phases of the request processing lifecycle. * * This allows for implementations to support different response generation technologies, as well as alternative * strategies for saving and restoring the state of each view. * * An implementation of this class must be thread-safe. *

* *

* Please see {@link StateManager} for information on how the ViewHandler interacts the * {@link StateManager}. *

* *

* Version 2 of the specification formally introduced the concept of View Declaration Language. A View * Declaration Language (VDL) is a syntax used to declare user interfaces comprised of instances of Jakarta Faces * {@link UIComponent}s. Any of the responsibilities of the ViewHandler that specifically deal with the VDL * sub-system are now the domain of the VDL implementation. These responsibilities are defined on the * {@link ViewDeclarationLanguage} class. The ViewHandler provides {@link #getViewDeclarationLanguage} as a * convenience method to access the VDL implementation given a viewId. *

* */ public abstract class ViewHandler { private static final Logger log = Logger.getLogger("jakarta.faces.application"); // ------------------------------------------------------ Manifest Constants /** *

* The key, in the session's attribute set, under which the response character encoding may be stored and retrieved. *

* */ public static final String CHARACTER_ENCODING_KEY = "jakarta.faces.request.charset"; /** *

* This is not anymore used since removal of support for Jakarta Pages. *

* @deprecated Use {@link #FACELETS_SUFFIX_PARAM_NAME} instead. */ @Deprecated(since = "4.0", forRemoval = true) public static final String DEFAULT_SUFFIX_PARAM_NAME = "jakarta.faces.DEFAULT_SUFFIX"; /** *

* This is not anymore used since removal of support for Jakarta Pages. *

* @deprecated Use {@link #DEFAULT_FACELETS_SUFFIX} instead. */ @Deprecated(since = "4.0", forRemoval = true) public static final String DEFAULT_SUFFIX = ".xhtml"; /** *

* If this param is set, and calling toLowerCase().equals("true") on a String representation of its value returns true, * the runtime must ensure that any XML comments in the Facelets source page are not delivered to the client. *

* * @since 2.0 */ public static final String FACELETS_SKIP_COMMENTS_PARAM_NAME = "jakarta.faces.FACELETS_SKIP_COMMENTS"; /** *

* Allow the web application to define a list of alternate suffixes for Facelet based XHTML pages containing Jakarta Faces * content. This list is a space separated list of values of the form * .<extension>. The first physical resource whose extension matches one of the configured * extensions will be the suffix used to create the view ID. If this init parameter is not specified, the default value is taken from the value of the constant * {@link #DEFAULT_FACELETS_SUFFIX} *

* * @since 2.0 */ public static final String FACELETS_SUFFIX_PARAM_NAME = "jakarta.faces.FACELETS_SUFFIX"; /** *

* The value to use for the default extension for Facelet based XHTML pages if the webapp is using url extension * mapping. *

* * @since 2.0 */ public static final String DEFAULT_FACELETS_SUFFIX = ".xhtml"; /** *

* Allow the web application to define a semicolon (;) separated list of strings that is used to forcibly declare that * certain pages in the application must be interpreted as using Facelets, regardless of their extension. Each entry in * the semicolon (;) separated list of strings is either a file extension, as in *.xhtml, or a resource * prefix (starting with '/' and interpreted as relative to the web application root), as in /user/*. The * latter class of entry can also take the form of /<filename>.<extension>* such as * /login.xhtml*. *

* * @since 2.0 */ public static final String FACELETS_VIEW_MAPPINGS_PARAM_NAME = "jakarta.faces.FACELETS_VIEW_MAPPINGS"; /** *

* The buffer size to set on the response when the ResponseWriter is generated. By default the value is 1024. A value of * -1 will not assign a buffer size on the response. This should be increased if you are using development mode in order * to guarantee that the response isn't partially rendered when an error is generated. *

* * @since 2.0 */ public static final String FACELETS_BUFFER_SIZE_PARAM_NAME = "jakarta.faces.FACELETS_BUFFER_SIZE"; /** *

* When a page is requested, what interval in seconds should the compiler * check for changes. If you don't want the compiler to check for changes once the page is compiled, then use a value of * -1. Setting a low refresh period helps during development to be able to edit pages in a running application. * If this value * is not specified, then the default depends on {@link jakarta.faces.application.ProjectStage}. If it is {@code Production}, * then runtime must act as if it is set to -1, else the runtime must act as if it is * set to 0. *

* * @since 2.0 */ public static final String FACELETS_REFRESH_PERIOD_PARAM_NAME = "jakarta.faces.FACELETS_REFRESH_PERIOD"; /** *

* If this param is set, the runtime must interpret it as a semicolon (;) separated list of paths, starting with "/" * (without the quotes). The runtime must interpret each entry in the list as a path relative to the web application * root and interpret the file found at that path as a facelet tag library, conforming to the facelet taglibrary schema * and expose the tags therein according to Section "Facelet Tag Library mechanism". *

* * * @since 2.0 */ public static final String FACELETS_LIBRARIES_PARAM_NAME = "jakarta.faces.FACELETS_LIBRARIES"; /** *

* A semicolon (;) delimitted list of class names of type jakarta.faces.view.facelets.TagDecorator, with a no-argument * constructor. These decorators will be loaded when the first request for a Facelets VDL view hits the ViewHandler for * page compilation. *

* * @since 2.0 */ public static final String FACELETS_DECORATORS_PARAM_NAME = "jakarta.faces.FACELETS_DECORATORS"; // ---------------------------------------------------------- Public Methods /** * *

* Initialize the view for the request processing lifecycle. *

* *

* This method must be called at the beginning of the Restore View Phase of the Request Processing Lifecycle. * It is responsible for performing any per-request initialization necessary to the operation of the lifycecle. *

* *

* The default implementation must perform the following actions. If {@link ExternalContext#getRequestCharacterEncoding} * returns null, call {@link #calculateCharacterEncoding} and pass the result, if non-null, * into the {@link ExternalContext#setRequestCharacterEncoding} method. If * {@link ExternalContext#getRequestCharacterEncoding} returns non-null take no action. *

* * @param context the Faces context. * @throws FacesException if a problem occurs setting the encoding, such as the * UnsupportedEncodingException thrown by the underlying Jakarta Servlet or Portlet technology when the * encoding is not supported. * */ public void initView(FacesContext context) throws FacesException { String originalEncoding = context.getExternalContext().getRequestCharacterEncoding(); String encoding = (originalEncoding != null) ? originalEncoding : calculateCharacterEncoding(context); if (encoding != null && context.getExternalContext().getSession(false) != null) { context.getExternalContext().getSessionMap().put(CHARACTER_ENCODING_KEY, encoding); } if (originalEncoding != null) { return; } if (encoding != null) { try { context.getExternalContext().setRequestCharacterEncoding(encoding); } catch (UnsupportedEncodingException e) { String message = "Can't set encoding to: " + encoding + " Exception:" + e.getMessage(); log.fine(message); throw new FacesException(message, e); } } } /** *

* Perform whatever actions are required to restore the view associated with * the specified {@link FacesContext} and viewId. It may delegate to the restoreView of the * associated {@link StateManager} to do the actual work of restoring the view. If there is no available state for the * specified viewId, return null. *

* *

* Otherwise, the default implementation must obtain a reference to the {@link ViewDeclarationLanguage} for this * viewId and call its {@link ViewDeclarationLanguage#restoreView} method, returning the result and not * swallowing any exceptions thrown by that method. *

* * @param context {@link FacesContext} for the current request * @param viewId the view identifier for the current request * @return the restored view root, or null. * @throws NullPointerException if context is null * @throws FacesException if a Jakarta Servlet error occurs */ public abstract UIViewRoot restoreView(FacesContext context, String viewId); /** *

* Create and return a new {@link UIViewRoot} instance initialized with * information from the argument FacesContext and viewId. * Locate the {@link ViewDeclarationLanguage} implementation for the VDL used in the * view. The argument viewId must be converted to a physical viewId that can refer to an * actual resource suitable for use by the ViewDeclarationLanguage * {@link ViewDeclarationLanguage#createView}, which must be called by this method. * * @param context the Faces context. * @param viewId the view id. * @throws NullPointerException if context is null * * @return the viewroot. */ public abstract UIViewRoot createView(FacesContext context, String viewId); /** *

* Perform whatever actions are required to render the response view to the * response object associated with the current {@link FacesContext}. *

* *

* Otherwise, the default implementation must obtain a reference to the {@link ViewDeclarationLanguage} for the * viewId of the argument viewToRender and call its {@link ViewDeclarationLanguage#renderView} * method, returning the result and not swallowing any exceptions thrown by that method. *

* * @param context {@link FacesContext} for the current request * @param viewToRender the view to render * * @throws IOException if an input/output error occurs * @throws NullPointerException if context or viewToRender is null * @throws FacesException if a Jakarta Servlet error occurs */ public abstract void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException; /** *

* Returns an appropriate {@link Locale} to use for this and subsequent requests for the current client. *

* * @param context {@link FacesContext} for the current request * @return the locale. * @throws NullPointerException if context is null */ public abstract Locale calculateLocale(FacesContext context); /** *

* Returns the correct character encoding to be used for this request. *

* *

* The following algorithm is employed. *

* *
    * *
  • *

    * Examine the Content-Type request header. If it has a charset parameter, extract it and * return that as the encoding. *

    *
  • * *
  • *

    * If no charset parameter was found, check for the existence of a session by calling * {@link ExternalContext#getSession(boolean)} passing false as the argument. If that method returns * true, get the session Map by calling {@link ExternalContext#getSessionMap} and look for a value under * the key given by the value of the symbolic constant {@link ViewHandler#CHARACTER_ENCODING_KEY}. If present, return * the value, converted to String. *

    *
  • * *
  • *

    * Otherwise, return null *

    *
  • * *
* * @param context the Faces context. * @return the character encoding, or null * @since 1.2 */ public String calculateCharacterEncoding(FacesContext context) { ExternalContext extContext = context.getExternalContext(); Map headerMap = extContext.getRequestHeaderMap(); String contentType = headerMap.get("Content-Type"); String charEnc = null; // Look for a charset in the Content-Type header first. if (contentType != null) { // See if this header had a charset String charsetStr = "charset="; int len = charsetStr.length(); int idx = contentType.indexOf(charsetStr); // If we have a charset in this Content-Type header AND it // has a non-zero length. if (idx != -1 && idx + len < contentType.length()) { charEnc = contentType.substring(idx + len); } } // failing that, look in the session for a previously saved one if (charEnc == null) { if (extContext.getSession(false) != null) { charEnc = (String) extContext.getSessionMap().get(CHARACTER_ENCODING_KEY); } } return charEnc; } /** *

* Return an appropriate renderKitId for this and subsequent requests from the current client. It is an * error for this method to return null. *

* *

* The default return value is {@link jakarta.faces.render.RenderKitFactory#HTML_BASIC_RENDER_KIT}. *

* * @param context {@link FacesContext} for the current request * @return the render kit id. * @throws NullPointerException if context is null */ public abstract String calculateRenderKitId(FacesContext context); /** *

* Derive and return the viewId from the current request, or the argument input by following the algorithm defined in * section 7.6.2 "Default ViewHandler Implementation" of the Jakarta Faces Specification Document. *

* *

* This method should work correctly when the FacesServlet is invoked via either a path mapping, * extension mapping or an exact match (mapping) as defined by Servlet.12.2. Note that * path mapping is also commonly known as prefix mapping (e.g. "/faces/*") and * extension mapping as suffix mapping (e.g. "*.xhtml"). An exact match is possible where * there's a servlet mapping with an exact URL pattern such as "/foo". *

* *

* The default implementation of this method simply returns requestViewId unchanged. *

* * @param context the FacesContext for this request * * @param requestViewId the viewId to derive, * @return the derived view id. * @since 2.0 */ public String deriveViewId(FacesContext context, String requestViewId) { return requestViewId; } /** *

* Derive and return the viewId from the current request, or the argument input by following the algorithm defined in * section 7.6.2 "Default ViewHandler Implementation" of the Jakarta Faces Specification Document. * Note that unlike deriveViewId(), this method does not require that a * physical view be present. *

* *

* This method should work correctly when the FacesServlet is invoked via either a path mapping, * extension mapping or an exact match (mapping) as defined by Servlet.12.2. Note that * path mapping is also commonly known as prefix mapping (e.g. "/faces/*") and * extension mapping as suffix mapping (e.g. "*.xhtml"). An exact match is possible where * there's a servlet mapping with an exact URL pattern such as "/foo". *

* *

* The default implementation of this method simply returns requestViewId unchanged. *

* * @param context the FacesContext for this request * * @param requestViewId the viewId to derive, * @return the derived logical view id. * @since 2.1 */ public String deriveLogicalViewId(FacesContext context, String requestViewId) { return requestViewId; } /** *

* If the value returned from this method is used as the file * argument to the four-argument constructor for java.net.URL (assuming appropriate values are used for the * first three arguments), then a client making a request to the toExternalForm() of that URL * will select the argument viewId for traversing the Jakarta Faces lifecycle. Please see * section 7.6.2 "Default ViewHandler Implementation" of the Jakarta Faces Specification Document * for the complete specification, especially for details related to view * protection using the {@link jakarta.faces.render.ResponseStateManager#NON_POSTBACK_VIEW_TOKEN_PARAM} * and the behavior when the current request is to a URL for which the * FacesServlet has an exact mapping as defined by Servlet.12.2. *

* * * @param context {@link FacesContext} for this request * @param viewId View identifier of the desired view * * @throws IllegalArgumentException if viewId is not valid for this ViewHandler, or does not * start with "/". * @throws NullPointerException if context or viewId is null. * * @return the action url. */ public abstract String getActionURL(FacesContext context, String viewId); /** *

* Return a Jakarta Faces action URL derived from the viewId argument that is suitable to be used by * the {@link NavigationHandler} to issue a redirect request to the URL using a NonFaces request. Compliant * implementations must implement this method as specified in * section 7.6.2 "Default ViewHandler Implementation" of the Jakarta Faces Specification Document. * The default implementation simply calls * through to {@link #getActionURL}, passing the arguments context and viewId. *

* * @param context The FacesContext processing this request * @param viewId The view identifier of the target page * @param parameters A mapping of parameter names to one or more values * @param includeViewParams A flag indicating whether view parameters should be encoded into this URL * @return the redirect URL. * @since 2.0 */ public String getRedirectURL(FacesContext context, String viewId, Map> parameters, boolean includeViewParams) { return getActionURL(context, viewId); } /** *

* Return a Jakarta Faces action URL derived from the viewId argument that is suitable to be used as the target * of a link in a Jakarta Faces response. Compliant implementations must implement this method as specified in * section 7.6.2 "Default ViewHandler Implementation" of the Jakarta Faces Specification Document. * The default implementation simply calls through to {@link #getActionURL}, passing the arguments * context and viewId. *

* * @param context The FacesContext processing this request * @param viewId The view identifier of the target page * @param parameters A mapping of parameter names to one or more values * @param includeViewParams A flag indicating whether view parameters should be encoded into this URL * * @return the bookmarkable URL. * * @since 2.0 */ public String getBookmarkableURL(FacesContext context, String viewId, Map> parameters, boolean includeViewParams) { return getActionURL(context, viewId); } /** *

* If the value returned from this method is used as the file argument to the four-argument constructor for * java.net.URL (assuming appropriate values are used for the first three arguments), then a client making * a request to the toExternalForm() of that URL will select the argument path * for direct rendering. If the specified path starts with a slash, it must be treated as context relative; otherwise, * it must be treated as relative to the action URL of the current view. *

* * @param context {@link FacesContext} for the current request * @param path Resource path to convert to a URL * * @throws IllegalArgumentException if viewId is not valid for this ViewHandler. * @throws NullPointerException if context or path is null. * * @return the resource URL. */ public abstract String getResourceURL(FacesContext context, String path); /** *

* If the value returned from this method is used as the file argument to the four-argument constructor for * java.net.URL (assuming appropriate values are used for the first three arguments), then a client making * a push handshake request to the toExternalForm() of that URL will select the argument * channel for connecting the websocket push channel in the current view. It must match the * {@link PushContext#URI_PREFIX} of the endpoint. *

* * @param context {@link FacesContext} for the current request. * @param channel The channel name of the websocket. * * @throws NullPointerException if context or channel is null. * * @return the websocket URL. * @see PushContext#URI_PREFIX */ public abstract String getWebsocketURL(FacesContext context, String channel); /** *

* Return an unmodifiable Set of the protected views currently known to this ViewHandler * instance. Compliant implementations must return a Set that is the concatenation of the contents of all * the <url-pattern> elements within all the <protected-views> in all of the * application configuration resources in the current application. The runtime must support calling this method at any * time after application startup. The default implementation returns an unmodifiable empty Set. *

* * @return the unmodifiable set of protected views. * @since 2.2 */ public Set getProtectedViewsUnmodifiable() { return unmodifiableSet(emptySet()); } /** *

* Add the argument urlPattern to the thread safe Set of protected views for this application. * Compliant implementations make it so a subsequent call to {@link #getProtectedViewsUnmodifiable} contains the * argument. The runtime must support calling this method at any time after application startup. The default * implementation takes no action. *

* * @param urlPattern the url-pattern to add. * * @since 2.2 */ public void addProtectedView(String urlPattern) { } /** *

* Remove the argument urlPattern from the thread safe Set of protected views for this * application, if present in the Set. If the argument urlPattern is not present in the * Set, this method has no effect. Compliant implementations must make it so a subsequent call to * {@link #getProtectedViewsUnmodifiable} does not contain the argument. The runtime must support calling this method at * any time after application startup. Returns true if this Set contained the argument. The * default implementation takes no action and returns false. *

* * @param urlPattern the url-pattern to remove. * @return true if in the Set, false otherwise. * @since 2.2 */ public boolean removeProtectedView(String urlPattern) { return false; } /** *

* Return the {@link ViewDeclarationLanguage} instance used for this * ViewHandler instance. *

* *
* *

* The default implementation must use * {@link jakarta.faces.view.ViewDeclarationLanguageFactory#getViewDeclarationLanguage} to obtain the appropriate * ViewDeclarationLanguage implementation for the argument viewId. Any exceptions thrown as a * result of invoking that method must not be swallowed. *

* *

* The default implementation of this method returns null. *

* *
* * @param context the FacesContext for this request. * * @param viewId the logical view id, as returned from {@link #deriveLogicalViewId} * for which the ViewDeclarationLanguage should be returned. * @return the ViewDeclarationLanguage, or null. * @since 2.0 */ public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId) { return null; } /** *

* Return a {@code Stream} possibly lazily populated by walking the view trees of every active * {@link ViewDeclarationLanguage} rooted at a given initial path. The view tree of every * {@link ViewDeclarationLanguage} is individually traversed breadth-first as per the contract of * {@link ViewDeclarationLanguage#getViews(FacesContext, String, int, ViewVisitOption...)}. The elements in the stream * are logical view ids. *

* *

* The {@code maxDepth} parameter is the maximum depth of directory levels to visit for each * {@code ViewDeclarationLanguage} beyond the initial path, which is always visited. The value is relative to * the root ({@code /}), not to the given initial path. E.g. given {@code maxDepth} = {@code 3} and initial path * {@code /foo/}, visiting will proceed up to {@code /foo/bar/}, where {@code /} counts as depth {@code 1}, * {@code /foo/} as depth {@code 2} and {@code /foo/bar/} as depth {@code 3}. A value lower or equal to the depth of the * initial path means that only the initial path is visited. A value of {@link Integer#MAX_VALUE MAX_VALUE} may be used * to indicate that all levels should be visited. * *

* In case more than one active {@code ViewDeclarationLanguage} is present, the order in which view ids from each * {@code ViewDeclarationLanguage} appear in the stream is undetermined, except for the guarantee that every individual * {@code ViewDeclarationLanguage} is traversed breadth-first. * * @param facesContext The {@link FacesContext} for this request. * @param path The initial path from which to start looking for view ids. * @param maxDepth The absolute maximum depth of nested directories to visit counted from the root ({@code /}). * @param options The options to influence the traversal. See {@link ViewVisitOption} for details on those. * * @return the {@link Stream} of view ids * * @since 2.3 */ public Stream getViews(FacesContext facesContext, String path, int maxDepth, ViewVisitOption... options) { return Stream.empty(); } /** *

* Return a {@code Stream} possibly lazily populated by walking the view trees of every active * {@link ViewDeclarationLanguage} rooted at a given initial path. The view tree of every * {@link ViewDeclarationLanguage} is individually traversed breadth-first as per the contract of * {@link ViewDeclarationLanguage#getViews(FacesContext, String, int, ViewVisitOption...)}. The elements in the stream * are logical view ids. *

* *

* This method works as if invoking it were equivalent to evaluating the expression:

* *
     * getViews(facesContext, start, Integer.MAX_VALUE, options)
     * 
* *
Put differently, it visits all levels of the view tree. * *

* In case more than one active {@code ViewDeclarationLanguage} is present, the order in which view ids from each * {@code ViewDeclarationLanguage} appear in the stream is undetermined, except for the guarantee that every individual * {@code ViewDeclarationLanguage} is traversed breadth-first. * * @param facesContext The {@link FacesContext} for this request. * @param path The initial path from which to start looking for view ids. * @param options The options to influence the traversal. See {@link ViewVisitOption} for details on those. * * @return the {@link Stream} of view ids * * @since 2.3 */ public Stream getViews(FacesContext facesContext, String path, ViewVisitOption... options) { return Stream.empty(); } /** *

* Take any appropriate action to either immediately write out the current state information (by calling * {@link StateManager#writeState}, or noting where state information should later be written. *

* *

* This method must do nothing if the current request is an Ajax request. When responding to * Ajax requests, the state is obtained by calling {@link StateManager#getViewState} and then written into * the Ajax response during final encoding * ({@link jakarta.faces.context.PartialViewContext#processPartial(jakarta.faces.event.PhaseId)}) . *

* * @param context {@link FacesContext} for the current request * * @throws IOException if an input/output error occurs * @throws NullPointerException if context is null */ public abstract void writeState(FacesContext context) throws IOException; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy