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

com.sun.faces.application.NavigationHandlerImpl Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.faces.application;

import javax.faces.FacesException;
import javax.faces.application.NavigationCase;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialViewContext;

import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.faces.util.MessageUtils;
import com.sun.faces.util.Util;
import com.sun.faces.util.FacesLogger;
import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIViewAction;
import javax.faces.context.Flash;

/**
 * 

NavigationHandlerImpl is the class that implements * default navigation handling. Refer to section 7.4.2 of the specification for * more details. * PENDING: Make independent of ApplicationAssociate. */ public class NavigationHandlerImpl extends ConfigurableNavigationHandler { // Log instance for this class private static final Logger logger = FacesLogger.APPLICATION.getLogger(); /** * Map containing configured navigation cases. */ private volatile NavigationMap navigationMap; /** * Flag indicated the current mode. */ private boolean development; private static final Pattern REDIRECT_EQUALS_TRUE = Pattern.compile("(.*)(faces-redirect=true)(.*)"); private static final Pattern INCLUDE_VIEW_PARAMS_EQUALS_TRUE = Pattern.compile("(.*)(includeViewParams=true)(.*)"); // ------------------------------------------------------------ Constructors /** * This constructor uses the current ApplicationAssociate * instance to obtain the navigation mappings used to make * navigational decisions. */ public NavigationHandlerImpl() { super(); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Created NavigationHandler instance "); } ApplicationAssociate associate = ApplicationAssociate.getInstance( FacesContext.getCurrentInstance().getExternalContext()); if (associate != null) { development = associate.isDevModeEnabled(); } } // ------------------------------ Methods from ConfigurableNavigationHandler /** * @see javax.faces.application.ConfigurableNavigationHandler#getNavigationCase(javax.faces.context.FacesContext, String, String) */ @Override public NavigationCase getNavigationCase(FacesContext context, String fromAction, String outcome) { Util.notNull("context", context); NavigationCase result = null; CaseStruct caseStruct = getViewId(context, fromAction, outcome); if (null != caseStruct) { result = caseStruct.navCase; } return result; } /** * @see javax.faces.application.ConfigurableNavigationHandler#getNavigationCases() */ @Override public Map> getNavigationCases() { if (navigationMap == null) { navigationMap = new NavigationMap(); } return navigationMap; } // ------------------------------------------ Methods from NavigationHandler /** * @see javax.faces.application.NavigationHandler#handleNavigation(javax.faces.context.FacesContext, String, String) */ public void handleNavigation(FacesContext context, String fromAction, String outcome) { Util.notNull("context", context); CaseStruct caseStruct = getViewId(context, fromAction, outcome); if (caseStruct != null) { ExternalContext extContext = context.getExternalContext(); ViewHandler viewHandler = Util.getViewHandler(context); assert (null != viewHandler); Flash flash = extContext.getFlash(); boolean isUIViewActionBroadcastAndViewdsDiffer = false; if (UIViewAction.isProcessingBroadcast(context)) { flash.setKeepMessages(true); String viewIdBefore = context.getViewRoot().getViewId(); viewIdBefore = (null == viewIdBefore) ? "" : viewIdBefore; String viewIdAfter = caseStruct.navCase.getToViewId(context); viewIdAfter = (null == viewIdAfter) ? "" : viewIdAfter; isUIViewActionBroadcastAndViewdsDiffer = !viewIdBefore.equals(viewIdAfter); } if (caseStruct.navCase.isRedirect() || isUIViewActionBroadcastAndViewdsDiffer) { // perform a 302 redirect. String redirectUrl = viewHandler.getRedirectURL(context, caseStruct.viewId, SharedUtils.evaluateExpressions(context, caseStruct.navCase.getParameters()), caseStruct.navCase.isIncludeViewParams()); try { if (logger.isLoggable(Level.FINE)) { logger.fine("Redirecting to path " + redirectUrl + " for outcome " + outcome + "and viewId " + caseStruct.viewId); } // encode the redirect to ensure session state // is maintained clearViewMapIfNecessary(context.getViewRoot(), caseStruct.viewId); updateRenderTargets(context, caseStruct.viewId); flash.setRedirect(true); extContext.redirect(redirectUrl); } catch (java.io.IOException ioe) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE,"jsf.redirect_failed_error", redirectUrl); } throw new FacesException(ioe.getMessage(), ioe); } context.responseComplete(); if (logger.isLoggable(Level.FINE)) { logger.fine("Response complete for " + caseStruct.viewId); } } else { UIViewRoot newRoot = viewHandler.createView(context, caseStruct.viewId); updateRenderTargets(context, caseStruct.viewId); context.setViewRoot(newRoot); if (logger.isLoggable(Level.FINE)) { logger.fine("Set new view in FacesContext for " + caseStruct.viewId); } } } } // --------------------------------------------------------- Private Methods private void initializeNavigationFromAssociate() { ApplicationAssociate associate = ApplicationAssociate.getCurrentInstance(); if (associate != null) { Map> m = associate.getNavigationCaseListMappings(); navigationMap = new NavigationMap(); if (m != null) { navigationMap.putAll(m); } } } /** * Calls clear() on the ViewMap (if available) if the view * ID of the UIViewRoot differs from newId */ private void clearViewMapIfNecessary(UIViewRoot root, String newId) { if (!root.getViewId().equals(newId)) { Map viewMap = root.getViewMap(false); if (viewMap != null) { viewMap.clear(); } } } private void updateRenderTargets(FacesContext ctx, String newId) { if (!ctx.getViewRoot().getViewId().equals(newId)) { PartialViewContext pctx = ctx.getPartialViewContext(); if (!pctx.isRenderAll()) { pctx.setRenderAll(true); } } } /** * This method uses helper methods to determine the new view identifier. * Refer to section 7.4.2 of the specification for more details. * * @param ctx the @{link FacesContext} for the current request * @param fromAction The action reference string * @param outcome The outcome string * @return The view identifier. */ private CaseStruct getViewId(FacesContext ctx, String fromAction, String outcome) { if (navigationMap == null) { synchronized (this) { initializeNavigationFromAssociate(); } } UIViewRoot root = ctx.getViewRoot(); String viewId = (root != null ? root.getViewId() : null); // if viewIdToTest is not null, use its value to find // a navigation match, otherwise look for a match // based soley on the fromAction and outcome CaseStruct caseStruct = null; if (viewId != null) { caseStruct = findExactMatch(ctx, viewId, fromAction, outcome); if (caseStruct == null) { caseStruct = findWildCardMatch(ctx, viewId, fromAction, outcome); } } if (caseStruct == null) { caseStruct = findDefaultMatch(ctx, fromAction, outcome); } // If the navigation rules do not have a match... if (caseStruct == null && outcome != null && viewId != null) { // Treat empty string equivalent to null outcome. JSF 2.0 Rev a // Changelog issue C063. if (caseStruct == null && 0 == outcome.length()) { outcome = null; } else { caseStruct = findImplicitMatch(ctx, viewId, fromAction, outcome); } } // no navigation case fo if (caseStruct == null && outcome != null && development) { String key; Object[] params; if (fromAction == null) { key = MessageUtils.NAVIGATION_NO_MATCHING_OUTCOME_ID; params = new Object[] { viewId, outcome }; } else { key = MessageUtils.NAVIGATION_NO_MATCHING_OUTCOME_ACTION_ID; params = new Object[] { viewId, fromAction, outcome }; } FacesMessage m = MessageUtils.getExceptionMessage(key, params); m.setSeverity(FacesMessage.SEVERITY_WARN); ctx.addMessage(null, m); } return caseStruct; } /** * This method finds the List of cases for the current view identifier. * After the cases are found, the from-action and from-outcome * values are evaluated to determine the new view identifier. * Refer to section 7.4.2 of the specification for more details. * * @param ctx the {@link FacesContext} for the current request * @param viewId The current view identifier. * @param fromAction The action reference string. * @param outcome The outcome string. * @return The view identifier. */ private CaseStruct findExactMatch(FacesContext ctx, String viewId, String fromAction, String outcome) { Set caseSet = navigationMap.get(viewId); if (caseSet == null) { return null; } // We've found an exact match for the viewIdToTest. Now we need to evaluate // from-action/outcome in the following order: // 1) elements specifying both from-action and from-outcome // 2) elements specifying only from-outcome // 3) elements specifying only from-action // 4) elements where both from-action and from-outcome are null return determineViewFromActionOutcome(ctx, caseSet, fromAction, outcome); } /** * This method traverses the wild card match List (containing from-view-id * strings and finds the List of cases for each from-view-id string. * Refer to section 7.4.2 of the specification for more details. * * @param ctx the {@link FacesContext} for the current request * @param viewId The current view identifier. * @param fromAction The action reference string. * @param outcome The outcome string. * @return The view identifier. */ private CaseStruct findWildCardMatch(FacesContext ctx, String viewId, String fromAction, String outcome) { CaseStruct result = null; for (String fromViewId : navigationMap.wildcardMatchList) { // See if the entire wildcard string (without the trailing "*" is // contained in the incoming viewIdToTest. // Ex: /foobar is contained with /foobarbaz // If so, then we have found our largest pattern match.. // If not, then continue on to the next case; if (!viewId.startsWith(fromViewId)) { continue; } // Append the trailing "*" so we can do our map lookup; String wcFromViewId = new StringBuilder(32).append(fromViewId).append('*').toString(); Set ccaseSet = navigationMap.get(wcFromViewId); if (ccaseSet == null) { return null; } // If we've found a match, then we need to evaluate // from-action/outcome in the following order: // 1) elements specifying both from-action and from-outcome // 2) elements specifying only from-outcome // 3) elements specifying only from-action // 4) elements where both from-action and from-outcome are null result = determineViewFromActionOutcome(ctx, ccaseSet, fromAction, outcome); if (result != null) { break; } } return result; } /** * This method will extract the cases for which a from-view-id is * an asterisk "*". * Refer to section 7.4.2 of the specification for more details. * * @param ctx the {@link FacesContext} for the current request * @param fromAction The action reference string. * @param outcome The outcome string. * @return The view identifier. */ private CaseStruct findDefaultMatch(FacesContext ctx, String fromAction, String outcome) { Set caseSet = navigationMap.get("*"); if (caseSet == null) { return null; } // We need to evaluate from-action/outcome in the follow // order: 1)elements specifying both from-action and from-outcome // 2) elements specifying only from-outcome // 3) elements specifying only from-action // 4) elements where both from-action and from-outcome are null return determineViewFromActionOutcome(ctx, caseSet, fromAction, outcome); } /** *

* Create a navigation case based on content within the outcome. *

* * @param context the {@link FacesContext} for the current request * @param viewId of the {@link UIViewRoot} for the current request * @param fromAction the navigation action * @param outcome the navigation outcome * @return a CaseStruct representing the the navigation result based * on the provided input */ private CaseStruct findImplicitMatch(FacesContext context, String viewId, String fromAction, String outcome) { // look for an implicit match. String viewIdToTest = outcome; String currentViewId = viewId; Map> parameters = null; boolean isRedirect = false; boolean isIncludeViewParams = false; int questionMark = viewIdToTest.indexOf('?'); String queryString; if (-1 != questionMark) { int viewIdLen = viewIdToTest.length(); if (viewIdLen <= (questionMark+1)) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "jsf.navigation_invalid_query_string", viewIdToTest); } if (development) { String key; Object[] params; key = MessageUtils.NAVIGATION_INVALID_QUERY_STRING_ID; params = new Object[]{viewIdToTest}; FacesMessage m = MessageUtils.getExceptionMessage(key, params); m.setSeverity(FacesMessage.SEVERITY_WARN); context.addMessage(null, m); } queryString = null; viewIdToTest = viewIdToTest.substring(0, questionMark); } else { queryString = viewIdToTest.substring(questionMark + 1); viewIdToTest = viewIdToTest.substring(0, questionMark); Matcher m = REDIRECT_EQUALS_TRUE.matcher(queryString); if (m.find()) { isRedirect = true; queryString = queryString.replace(m.group(2), ""); } m = INCLUDE_VIEW_PARAMS_EQUALS_TRUE.matcher(queryString); if (m.find()) { isIncludeViewParams = true; queryString = queryString.replace(m.group(2), ""); } } if (queryString != null && queryString.length() > 0) { String[] queryElements = Util.split(queryString, "&|&"); for (int i = 0, len = queryElements.length; i < len; i ++) { String[] elements = Util.split(queryElements[i], "="); if (elements.length == 2) { if (parameters == null) { parameters = new LinkedHashMap>(len / 2, 1.0f); List values = new ArrayList(2); values.add(elements[1]); parameters.put(elements[0], values); } else { List values = parameters.get(elements[0]); if (values == null) { values = new ArrayList(2); parameters.put(elements[0], values); } values.add(elements[1]); } } } } } // If the viewIdToTest needs an extension, take one from the currentViewId. if (viewIdToTest.lastIndexOf('.') == -1) { int idx = currentViewId.lastIndexOf('.'); if (idx != -1) { viewIdToTest = viewIdToTest + currentViewId.substring(idx); } } if (!viewIdToTest.startsWith("/")) { int lastSlash = currentViewId.lastIndexOf("/"); if (lastSlash != -1) { currentViewId = currentViewId.substring(0, lastSlash + 1); viewIdToTest = currentViewId + viewIdToTest; } else { viewIdToTest = "/" + viewIdToTest; } } ViewHandler viewHandler = Util.getViewHandler(context); viewIdToTest = viewHandler.deriveViewId(context, viewIdToTest); if (null != viewIdToTest) { CaseStruct caseStruct = new CaseStruct(); caseStruct.viewId = viewIdToTest; caseStruct.navCase = new NavigationCase(currentViewId, fromAction, outcome, null, viewIdToTest, parameters, isRedirect, isIncludeViewParams); return caseStruct; } return null; } /** * This method will attempt to find the view identifier based on action reference * and outcome. Refer to section 7.4.2 of the specification for more details. * @param ctx the {@link FacesContext} for the current request * @param caseSet The list of navigation cases. * @param fromAction The action reference string. * @param outcome The outcome string. * @return The view identifier. */ private CaseStruct determineViewFromActionOutcome(FacesContext ctx, Set caseSet, String fromAction, String outcome) { CaseStruct result = new CaseStruct(); boolean match = false; for (NavigationCase cnc : caseSet) { String cncFromAction = cnc.getFromAction(); String cncFromOutcome = cnc.getFromOutcome(); boolean cncHasCondition = cnc.hasCondition(); String cncToViewId = cnc.getToViewId(ctx); if ((cncFromAction != null) && (cncFromOutcome != null)) { if ((cncFromAction.equals(fromAction)) && (cncFromOutcome.equals(outcome))) { result.viewId = cncToViewId; result.navCase = cnc; match = true; } } else if ((cncFromAction == null) && (cncFromOutcome != null)) { if (cncFromOutcome.equals(outcome)) { result.viewId = cncToViewId; result.navCase = cnc; match = true; } } else if ((cncFromAction != null) && (cncFromOutcome == null)) { if (cncFromAction.equals(fromAction) && (outcome != null || cncHasCondition)) { result.viewId = cncToViewId; result.navCase = cnc; match = true; } } else if ((cncFromAction == null) && (cncFromOutcome == null)) { if (outcome != null || cncHasCondition) { result.viewId = cncToViewId; result.navCase = cnc; match = true; } } if (match) { if (cncHasCondition && Boolean.FALSE.equals(cnc.getCondition(ctx))) { match = false; } else { return result; } } } return null; } // ---------------------------------------------------------- Nested Classes private static class CaseStruct { String viewId; NavigationCase navCase; } private static final class NavigationMap extends AbstractMap> { private HashMap> navigationMap = new HashMap>(); private TreeSet wildcardMatchList = new TreeSet(new Comparator() { public int compare(String fromViewId1, String fromViewId2) { return -(fromViewId1.compareTo(fromViewId2)); } }); // ---------------------------------------------------- Methods from Map @Override public int size() { return navigationMap.size(); } @Override public boolean isEmpty() { return navigationMap.isEmpty(); } @Override public Set put(String key, Set value) { if (key == null) { throw new IllegalArgumentException(key); } if (value == null) { throw new IllegalArgumentException(); } updateWildcards(key); Set existing = navigationMap.get(key); if (existing == null) { navigationMap.put(key, value); return null; } else { existing.addAll(value); return existing; } } @Override public void putAll(Map> m) { if (m == null) { return; } for (Map.Entry> entry : m.entrySet()) { String key = entry.getKey(); updateWildcards(key); Set existing = navigationMap.get(key); if (existing == null) { navigationMap.put(key, entry.getValue()); } else { existing.addAll(entry.getValue()); } } } @Override public Set keySet() { return new AbstractSet() { public Iterator iterator() { return new Iterator() { Iterator>> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public String next() { return i.next().getKey(); } public void remove() { throw new UnsupportedOperationException(); } }; } public int size() { return NavigationMap.this.size(); } }; } @Override public Collection> values() { return new AbstractCollection>() { public Iterator> iterator() { return new Iterator>() { Iterator>> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public Set next() { return i.next().getValue(); } public void remove() { throw new UnsupportedOperationException(); } }; } public int size() { return NavigationMap.this.size(); } }; } public Set>> entrySet() { return new AbstractSet>>() { public Iterator>> iterator() { return new Iterator>>() { Iterator>> i = navigationMap.entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public Entry> next() { return i.next(); } public void remove() { throw new UnsupportedOperationException(); } }; } public int size() { return NavigationMap.this.size(); } }; } // ----------------------------------------------------- Private Methods private void updateWildcards(String fromViewId) { if (!navigationMap.containsKey(fromViewId)) { if (fromViewId.endsWith("*")) { wildcardMatchList.add(fromViewId.substring(0, fromViewId.lastIndexOf('*'))); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy