Permalink
Browse files

Fix multiviews fail when configured on subfolder without welcome file

  • Loading branch information...
1 parent f261525 commit 82ba9720de8c5416cbf635952816b141a3523bc4 @BalusC BalusC committed Nov 18, 2016
@@ -37,7 +37,6 @@
import static org.omnifaces.util.ResourcePaths.stripPrefixPath;
import static org.omnifaces.util.Servlets.getApplicationAttribute;
import static org.omnifaces.util.Servlets.getRequestBaseURL;
-import static org.omnifaces.util.Servlets.isFacesDevelopment;
import static org.omnifaces.util.Utils.csvToList;
import static org.omnifaces.util.Utils.isEmpty;
import static org.omnifaces.util.Utils.reverse;
@@ -200,7 +199,9 @@
* The name of the enum context parameter that determines the method used by FacesViews to invoke the FacesServlet.
* See {@link FacesServletDispatchMethod}.
* @see FacesServletDispatchMethod
+ * @deprecated This will be determined automatically.
*/
+ @Deprecated
public static final String FACES_VIEWS_DISPATCH_METHOD_PARAM_NAME = "org.omnifaces.FACES_VIEWS_DISPATCH_METHOD";
/**
@@ -268,7 +269,6 @@ public static void registerForwardingFilter(ServletContext servletContext) {
Map<String, String> collectedViews = scanAndStoreViews(servletContext, true);
if (!collectedViews.isEmpty()) {
- FacesServletDispatchMethod dispatchMethod = getFacesServletDispatchMethod(servletContext);
boolean filterAfterDeclaredFilters = parseBoolean(servletContext.getInitParameter(FACES_VIEWS_FILTER_AFTER_DECLARED_FILTERS_PARAM_NAME));
// Register a Filter that forwards extensionless requests to an extension mapped request, e.g. /index to /index.xhtml
@@ -279,33 +279,22 @@ public static void registerForwardingFilter(ServletContext servletContext) {
// TODO: Migrate ResourceResolver to ResourceHandler.
servletContext.setInitParameter(FACELETS_RESOURCE_RESOLVER_PARAM_NAME, FacesViewsResolver.class.getName());
- if (isFacesDevelopment(servletContext) && dispatchMethod != DO_FILTER) {
+ boolean multiViewsWelcomeFiles = isMultiViewsEnabled(servletContext) && !getMappedWelcomeFiles(servletContext).isEmpty();
- // In development mode map this Filter to "/*", so we can catch requests to extensionless resources that have been dynamically added.
- // Note that resources with mapped extensions are already handled by the FacesViewsResolver.
- // Adding resources with new extensions still requires a restart.
-
- // Development mode only works when the dispatch mode is not DO_FILTER,
- // since DO_FILTER mode depends on the Faces Servlet being "exact"-mapped on the view resources.
-
- filterRegistration.addMappingForUrlPatterns(null, filterAfterDeclaredFilters, "/*");
+ if (multiViewsWelcomeFiles) {
+ // When MultiViews is enabled and there are mapped welcome files, we need to filter on /* otherwise path params won't work on root.
+ filterRegistration.addMappingForUrlPatterns(EnumSet.of(REQUEST, FORWARD), filterAfterDeclaredFilters, "/*");
}
else {
- if (isMultiViewsEnabled(servletContext) && !getMappedWelcomeFiles(servletContext).isEmpty()) {
- // When MultiViews is enabled and there are mapped welcome files, we need to filter on /* otherwise path params won't work on root.
- filterRegistration.addMappingForUrlPatterns(EnumSet.of(REQUEST, FORWARD), filterAfterDeclaredFilters, "/*");
+ // Map the forwarding filter to all the resources we found.
+ for (String mapping : collectedViews.keySet()) {
+ filterRegistration.addMappingForUrlPatterns(EnumSet.of(REQUEST, FORWARD), filterAfterDeclaredFilters, mapping);
}
- else {
- // Map the forwarding filter to all the resources we found.
- for (String mapping : collectedViews.keySet()) {
- filterRegistration.addMappingForUrlPatterns(EnumSet.of(REQUEST, FORWARD), filterAfterDeclaredFilters, mapping);
- }
- // Additionally map the filter to all paths that were scanned and which are also directly accessible.
- // This is to give the filter an opportunity to block these.
- for (String path : getPublicRootPaths(servletContext)) {
- filterRegistration.addMappingForUrlPatterns(null, false, path + "*");
- }
+ // Additionally map the filter to all paths that were scanned and which are also directly accessible.
+ // This is to give the filter an opportunity to block these.
+ for (String path : getPublicRootPaths(servletContext)) {
+ filterRegistration.addMappingForUrlPatterns(null, false, path + "*");
}
}
@@ -373,6 +362,24 @@ public static void registerViewHander(ServletContext servletContext) {
* @return The views found during scanning, or an empty map if no views encountered.
*/
static Map<String, String> scanAndStoreViews(ServletContext servletContext, boolean collectExtensions) {
+ Set<String> mappedWelcomeFiles = new HashSet<>();
+
+ for (String welcomeFile : WebXml.INSTANCE.init(servletContext).getWelcomeFiles()) {
+ if (isExtensionless(welcomeFile)) {
+ if (!welcomeFile.startsWith("/")) {
+ welcomeFile = "/" + welcomeFile;
+ }
+
+ if (welcomeFile.endsWith("/")) {
+ welcomeFile = welcomeFile.substring(0, welcomeFile.length() - 1);
+ }
+
+ mappedWelcomeFiles.add(welcomeFile);
+ }
+ }
+
+ servletContext.setAttribute(MAPPED_WELCOME_FILES, unmodifiableSet(mappedWelcomeFiles));
+
Map<String, String> collectedViews = new HashMap<>();
Set<String> collectedExtensions = new HashSet<>();
@@ -388,25 +395,14 @@ public static void registerViewHander(ServletContext servletContext) {
if (collectExtensions) {
servletContext.setAttribute(ENCOUNTERED_EXTENSIONS, unmodifiableSet(collectedExtensions));
- Set<String> mappedWelcomeFiles = new HashSet<>();
if (!collectedExtensions.isEmpty()) {
- for (String welcomeFile : WebXml.INSTANCE.init(servletContext).getWelcomeFiles()) {
- if (isExtensionless(welcomeFile)) {
- if (!welcomeFile.startsWith("/")) {
- welcomeFile = "/" + welcomeFile;
- }
-
- mappedWelcomeFiles.add(welcomeFile);
-
- if (isMultiViewsEnabled(servletContext) && collectedViews.containsKey(welcomeFile + "/*")) {
- servletContext.setAttribute(MULTIVIEWS_WELCOME_FILE, welcomeFile);
- }
+ for (String welcomeFile : getMappedWelcomeFiles(servletContext)) {
+ if (isMultiViewsEnabled(servletContext) && collectedViews.containsKey(welcomeFile + "/*")) {
+ servletContext.setAttribute(MULTIVIEWS_WELCOME_FILE, welcomeFile);
}
}
}
-
- servletContext.setAttribute(MAPPED_WELCOME_FILES, unmodifiableSet(mappedWelcomeFiles));
}
}
@@ -488,6 +484,8 @@ private static void scanViews(ServletContext servletContext, String rootPath, Se
Map<String, String> collectedViews, String extensionToScan, Set<String> collectedExtensions)
{
if (!isEmpty(resourcePaths)) {
+ boolean multiViewsWelcomeFiles = isMultiViewsEnabled(servletContext) && !getMappedWelcomeFiles(servletContext).isEmpty();
+
for (String resourcePath : resourcePaths) {
if (isDirectory(resourcePath)) {
if (canScanDirectory(rootPath, resourcePath)) {
@@ -503,7 +501,17 @@ else if (canScanResource(resourcePath, extensionToScan)) {
// Store the resource with and without an extension, e.g. store both foo.xhtml and foo
collectedViews.put(resource, resourcePath);
String extensionlessResource = stripExtension(resource);
- collectedViews.put(extensionlessResource + (isMultiViewsEnabled(servletContext, extensionlessResource) ? "/*" : ""), resourcePath);
+
+ if (isMultiViewsEnabled(servletContext, extensionlessResource)) {
+ collectedViews.put(extensionlessResource + "/*", resourcePath);
+ }
+ else {
+ if (multiViewsWelcomeFiles) { // This will install forwarding filter on /* and therefore we need to cover / ourselves.
+ collectedViews.put(extensionlessResource + "/", resourcePath);
+ }
+
+ collectedViews.put(extensionlessResource, resourcePath);
+ }
// Optionally, collect all unique extensions that we have encountered.
if (collectedExtensions != null) {
@@ -15,6 +15,7 @@
import static javax.faces.application.ProjectStage.Development;
import static javax.servlet.http.HttpServletResponse.SC_MOVED_PERMANENTLY;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static org.omnifaces.facesviews.FacesServletDispatchMethod.DO_FILTER;
import static org.omnifaces.facesviews.FacesViews.FACES_VIEWS_ORIGINAL_PATH_INFO;
import static org.omnifaces.facesviews.FacesViews.FACES_VIEWS_ORIGINAL_SERVLET_PATH;
import static org.omnifaces.facesviews.FacesViews.getExtensionAction;
@@ -93,10 +94,6 @@ public void init() throws ServletException {
public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws ServletException, IOException {
String servletPath = request.getServletPath();
- if (servletPath.endsWith("/")) {
- servletPath = servletPath.substring(0, servletPath.length() - 1);
- }
-
if (filterExtensionLess(request, response, chain, servletPath)) {
return;
}
@@ -122,15 +119,16 @@ private boolean filterExtensionLess(HttpServletRequest request, HttpServletRespo
ServletContext servletContext = getServletContext();
boolean multiViews = isMultiViewsEnabled(request);
Map<String, String> resources = getMappedResources(servletContext);
- String resource = servletPath + (multiViews ? "/*" : "");
+ String normalizedServletPath = servletPath.endsWith("/") ? servletPath.substring(0, servletPath.length() - 1) : servletPath;
+ String resource = normalizedServletPath + (multiViews ? "/*" : "");
String pathInfo = coalesce(request.getPathInfo(), (String) request.getAttribute(FACES_VIEWS_ORIGINAL_PATH_INFO));
if (getApplicationFromFactory().getProjectStage() == Development && !resources.containsKey(resource)) {
// Check if the resource was dynamically added by scanning the faces-views location(s) again.
resources = scanAndStoreViews(servletContext, false);
}
- if (!resources.containsKey(resource) && multiViews) {
+ if (multiViews && !resources.containsKey(resource)) {
resource = getMultiViewsWelcomeFile(servletContext);
if (resource != null) {
@@ -141,48 +139,49 @@ private boolean filterExtensionLess(HttpServletRequest request, HttpServletRespo
}
if (resources.containsKey(resource)) {
- String normalizedResource = servletPath;
// Check if a welcome file was explicitly requested.
- if ((getRequestRelativeURI(request) + "/").startsWith(servletPath + "/")) {
- normalizedResource = stripWelcomeFilePrefix(servletContext, servletPath);
- }
+ if ((getRequestRelativeURI(request) + "/").startsWith(normalizedServletPath + "/")) {
+ String normalizedResource = stripWelcomeFilePrefix(servletContext, servletPath);
- if (!request.getServletPath().equals(normalizedResource)) {
- String uri = request.getContextPath() + normalizedResource;
- String queryString = request.getQueryString();
- redirectPermanent(response, uri + ((queryString != null) ? "?" + queryString : ""));
- return true;
+ if (!servletPath.equals(normalizedResource)) {
+
+ // If so, redirect back to parent folder.
+ String uri = request.getContextPath() + normalizedResource;
+ String queryString = request.getQueryString();
+ redirectPermanent(response, uri + ((queryString != null) ? "?" + queryString : ""));
+ return true;
+ }
}
- String extension = getExtension(resources.get(resource));
-
- switch (dispatchMethod) {
- case DO_FILTER:
- // Continue the chain, but make the request appear to be to the resource with an extension.
- // This assumes that the FacesServlet has been mapped to something that includes the extensionless
- // request.
- try {
- request.setAttribute(FACES_VIEWS_ORIGINAL_SERVLET_PATH, servletPath);
- request.setAttribute(FACES_VIEWS_ORIGINAL_PATH_INFO, pathInfo);
- chain.doFilter(new UriExtensionRequestWrapper(request, servletPath, extension), response);
- }
- finally {
- request.removeAttribute(FACES_VIEWS_ORIGINAL_SERVLET_PATH);
- request.removeAttribute(FACES_VIEWS_ORIGINAL_PATH_INFO);
- }
+ String servletPathWithExtension = normalizedServletPath + getExtension(resources.get(resource));
+
+ if (dispatchMethod == DO_FILTER && resources.containsKey(servletPathWithExtension)) {
+ // Continue the chain, but make the request appear to be to the resource with an extension.
+ // This assumes that the FacesServlet has been mapped to something that includes the extensionless
+ // request.
+ try {
+ request.setAttribute(FACES_VIEWS_ORIGINAL_SERVLET_PATH, servletPath);
+ request.setAttribute(FACES_VIEWS_ORIGINAL_PATH_INFO, pathInfo);
+ chain.doFilter(new UriExtensionRequestWrapper(request, servletPathWithExtension), response);
+ }
+ finally {
+ request.removeAttribute(FACES_VIEWS_ORIGINAL_SERVLET_PATH);
+ request.removeAttribute(FACES_VIEWS_ORIGINAL_PATH_INFO);
+ }
+ return true;
+ }
+ else {
+ // Forward the resource (view) using its original extension, on which the Facelets Servlet
+ // is mapped. Technically it matters most that the Facelets Servlet picks up the
+ // request, and the exact extension or even prefix is perhaps less relevant.
+ RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher(servletPathWithExtension);
+
+ if (requestDispatcher != null) {
+ requestDispatcher.forward(request, response);
return true;
- case FORWARD:
- // Forward the resource (view) using its original extension, on which the Facelets Servlet
- // is mapped. Technically it matters most that the Facelets Servlet picks up the
- // request, and the exact extension or even prefix is perhaps less relevant.
- RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher(servletPath + extension);
-
- if (requestDispatcher != null) {
- requestDispatcher.forward(request, response);
- return true;
- }
+ }
}
}
@@ -35,9 +35,9 @@
private final String servletPath;
- public UriExtensionRequestWrapper(HttpServletRequest request, String servletPath, String extension) {
+ public UriExtensionRequestWrapper(HttpServletRequest request, String servletPath) {
super(request);
- this.servletPath = servletPath.endsWith(extension) ? servletPath : servletPath + extension;
+ this.servletPath = servletPath;
}
@Override
@@ -277,16 +277,6 @@
* </tr>
*
* <tr>
- * <td class="colFirst"><code>{@value org.omnifaces.facesviews.FacesViews#FACES_VIEWS_DISPATCH_METHOD_PARAM_NAME}</code></td>
- * <td>Determines the method used by FacesViews to invoke the FacesServlet.
- * <br>Allowed values are enumerated in {@link org.omnifaces.facesviews.FacesServletDispatchMethod}, which have the following meaning:
- * <br>- <code>DO_FILTER</code>: Use a plain {@link javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} to invoke the {@link javax.faces.webapp.FacesServlet}. Using this method necessitates the FacesServlet to be mapped to the (extensionless) requested resource or to everything (/*) when manually mapping.
- * <br>- <code>FORWARD</code>: Use a forward to invoke the {@link javax.faces.webapp.FacesServlet}. Using this method the {@link javax.faces.webapp.FacesServlet} does not have to be mapped to the (extensionless) requested resource or to everything (/*) when manually mapping.
- * <br>Default value: <code>DO_FILTER</code>
- * </td>
- * </tr>
- *
- * <tr>
* <td class="colFirst"><code>{@value org.omnifaces.facesviews.FacesViews#FACES_VIEWS_VIEW_HANDLER_MODE_PARAM_NAME}</code></td>
* <td>Determines how the {@link org.omnifaces.facesviews.FacesViewsViewHandler} should build the action URL that's used in e.g. forms and links.
* <br>Allowed values are enumerated in {@link org.omnifaces.facesviews.ViewHandlerMode}, which have the following meaning:

0 comments on commit 82ba972

Please sign in to comment.