/* * $Id: NavigationHandlerImpl.java,v 1.3 2006/06/07 03:40:24 ssilvert Exp $ */ /* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://javaserverfaces.dev.java.net/CDDL.html or * legal/CDDLv1.0.txt. * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * [Name of File] [ver.__] [Date] * * Copyright 2005 Sun Microsystems Inc. All Rights Reserved */ package com.sun.faces.application; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.faces.FacesException; import javax.faces.FactoryFinder; import javax.faces.application.ApplicationFactory; import javax.faces.application.NavigationHandler; import javax.faces.application.ViewHandler; import javax.faces.component.UIViewRoot; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import com.sun.faces.config.ConfigureListener; import com.sun.faces.util.Util; import com.sun.faces.util.MessageUtils; /** *
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 NavigationHandler {
//
// Protected Constants
//
// Log instance for this class
private static Logger logger = Util.getLogger(Util.FACES_LOGGER
+ Util.APPLICATION_LOGGER);
//
// Class Variables
//
// Instance Variables
/**
* Map
containing configured navigation cases.
*/
private MapSet
containing wildcard navigation cases.
*/
private SetApplication
* 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 ");
}
// if the user is using the decorator pattern, this would cause
// our ApplicationAssociate to be created, if it isn't already
// created.
ApplicationFactory aFactory = (ApplicationFactory)
FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
aFactory.getApplication();
ApplicationAssociate associate = ApplicationAssociate.getInstance(
ConfigureListener.getExternalContextDuringInitialize());
if (associate != null) {
caseListMap = associate.getNavigationCaseListMappings();
wildCardSet = associate.getNavigationWildCardList();
navigationConfigured = (wildCardSet != null &&
caseListMap != null);
}
}
/**
* Determine the next view based on the current view
* (from-view-id
stored in FacesContext
),
* fromAction
and outcome
.
*
* @param context The FacesContext
* @param fromAction the action reference string
* @param outcome the outcome string
*/
public void handleNavigation(FacesContext context, String fromAction,
String outcome) {
if (context == null) {
String message = MessageUtils.getExceptionMessageString
(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context");
throw new NullPointerException(message);
}
if (outcome == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("No navigation rule found for null outcome "
+ "and viewId " +
context.getViewRoot().getViewId() +
" Explicitly remain on the current view ");
}
return; // Explicitly remain on the current view
}
CaseStruct caseStruct = getViewId(context, fromAction, outcome);
ExternalContext extContext = context.getExternalContext();
if (caseStruct != null) {
ViewHandler viewHandler = Util.getViewHandler(context);
assert (null != viewHandler);
if (caseStruct.navCase.hasRedirect()) {
// perform a 302 redirect.
String newPath =
viewHandler.getActionURL(context, caseStruct.viewId);
newPath = addRedirectParams(context, newPath, caseStruct.navCase);
try {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Redirecting to path " + newPath
+ " for outcome " + outcome +
"and viewId " + caseStruct.viewId);
}
extContext.redirect(newPath);
} catch (java.io.IOException ioe) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE,"jsf.redirect_failed_error",
newPath);
}
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);
context.setViewRoot(newRoot);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Set new view in FacesContext for " +
caseStruct.viewId);
}
}
}
}
/**
* If redirect params were specified, add them to the path. Otherwise,
* return the path unchanged.
*/
private String addRedirectParams(FacesContext context,
String path,
ConfigNavigationCase navCase) {
Mapview
identifier.
* Refer to section 7.4.2 of the specification for more details.
*
* @param context The Faces Context
* @param fromAction The action reference string
* @param outcome The outcome string
* @return The view
identifier.
*/
private CaseStruct getViewId(FacesContext context, String fromAction,
String outcome) {
String viewId = context.getViewRoot().getViewId();
CaseStruct caseStruct;
caseStruct = findExactMatch(viewId, fromAction, outcome);
if (caseStruct == null) {
caseStruct = findWildCardMatch(viewId, fromAction, outcome);
}
if (caseStruct == null) {
caseStruct = findDefaultMatch(fromAction, outcome);
}
if (caseStruct == null && logger.isLoggable(Level.WARNING)) {
if (fromAction == null) {
logger.log(Level.FINE,
"jsf.navigation.no_matching_outcome",
new Object[] {viewId, outcome});
} else {
logger.log(Level.FINE,
"jsf.navigation.no_matching_outcome_action",
new Object[] {viewId, outcome, fromAction});
}
}
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 viewId The current view
identifier.
* @param fromAction The action reference string.
* @param outcome The outcome string.
* @return The view
identifier.
*/
private CaseStruct findExactMatch(String viewId,
String fromAction,
String outcome) {
// if the user has elected to replace the Application instance
// entirely
if (!navigationConfigured) {
return null;
}
Listfrom-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 viewId The current view
identifier.
* @param fromAction The action reference string.
* @param outcome The outcome string.
* @return The view
identifier.
*/
private CaseStruct findWildCardMatch(String viewId,
String fromAction,
String outcome) {
CaseStruct result = null;
// if the user has elected to replace the Application instance
// entirely
if (!navigationConfigured) {
return null;
}
for (String fromViewId : wildCardSet) {
// See if the entire wildcard string (without the trailing "*" is
// contained in the incoming viewId.
// 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.indexOf(fromViewId, 0) == -1) {
continue;
}
// Append the trailing "*" so we can do our map lookup;
String wcFromViewId = fromViewId + '*';
Listfrom-view-id
is
* an asterisk "*".
* Refer to section 7.4.2 of the specification for more details.
*
* @param fromAction The action reference string.
* @param outcome The outcome string.
* @return The view
identifier.
*/
private CaseStruct findDefaultMatch(String fromAction,
String outcome) {
// if the user has elected to replace the Application instance
// entirely
if (!navigationConfigured) {
return null;
}
Listview
identifier based on action reference
* and outcome. Refer to section 7.4.2 of the specification for more details.
*
* @param caseList The list of navigation cases.
* @param fromAction The action reference string.
* @param outcome The outcome string.
* @return The view
identifier.
*/
private CaseStruct determineViewFromActionOutcome(List