001/* 002 * Copyright 2002-2012 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.jsf; 018 019import javax.faces.application.NavigationHandler; 020import javax.faces.context.FacesContext; 021 022import org.springframework.lang.Nullable; 023 024/** 025 * Base class for JSF NavigationHandler implementations that want 026 * to be capable of decorating an original NavigationHandler. 027 * 028 * <p>Supports the standard JSF style of decoration (through a constructor argument) 029 * as well as an overloaded {@code handleNavigation} method with explicit 030 * NavigationHandler argument (passing in the original NavigationHandler). Subclasses 031 * are forced to implement this overloaded {@code handleNavigation} method. 032 * Standard JSF invocations will automatically delegate to the overloaded method, 033 * with the constructor-injected NavigationHandler as argument. 034 * 035 * @author Juergen Hoeller 036 * @since 1.2.7 037 * @see #handleNavigation(javax.faces.context.FacesContext, String, String, NavigationHandler) 038 * @see DelegatingNavigationHandlerProxy 039 */ 040public abstract class DecoratingNavigationHandler extends NavigationHandler { 041 042 @Nullable 043 private NavigationHandler decoratedNavigationHandler; 044 045 046 /** 047 * Create a DecoratingNavigationHandler without fixed original NavigationHandler. 048 */ 049 protected DecoratingNavigationHandler() { 050 } 051 052 /** 053 * Create a DecoratingNavigationHandler with fixed original NavigationHandler. 054 * @param originalNavigationHandler the original NavigationHandler to decorate 055 */ 056 protected DecoratingNavigationHandler(NavigationHandler originalNavigationHandler) { 057 this.decoratedNavigationHandler = originalNavigationHandler; 058 } 059 060 /** 061 * Return the fixed original NavigationHandler decorated by this handler, if any 062 * (that is, if passed in through the constructor). 063 */ 064 @Nullable 065 public final NavigationHandler getDecoratedNavigationHandler() { 066 return this.decoratedNavigationHandler; 067 } 068 069 070 /** 071 * This implementation of the standard JSF {@code handleNavigation} method 072 * delegates to the overloaded variant, passing in constructor-injected 073 * NavigationHandler as argument. 074 * @see #handleNavigation(javax.faces.context.FacesContext, String, String, javax.faces.application.NavigationHandler) 075 */ 076 @Override 077 public final void handleNavigation(FacesContext facesContext, String fromAction, String outcome) { 078 handleNavigation(facesContext, fromAction, outcome, this.decoratedNavigationHandler); 079 } 080 081 /** 082 * Special {@code handleNavigation} variant with explicit NavigationHandler 083 * argument. Either called directly, by code with an explicit original handler, 084 * or called from the standard {@code handleNavigation} method, as 085 * plain JSF-defined NavigationHandler. 086 * <p>Implementations should invoke {@code callNextHandlerInChain} to 087 * delegate to the next handler in the chain. This will always call the most 088 * appropriate next handler (see {@code callNextHandlerInChain} javadoc). 089 * Alternatively, the decorated NavigationHandler or the passed-in original 090 * NavigationHandler can also be called directly; however, this is not as 091 * flexible in terms of reacting to potential positions in the chain. 092 * @param facesContext the current JSF context 093 * @param fromAction the action binding expression that was evaluated to retrieve the 094 * specified outcome, or {@code null} if the outcome was acquired by some other means 095 * @param outcome the logical outcome returned by a previous invoked application action 096 * (which may be {@code null}) 097 * @param originalNavigationHandler the original NavigationHandler, 098 * or {@code null} if none 099 * @see #callNextHandlerInChain 100 */ 101 public abstract void handleNavigation(FacesContext facesContext, @Nullable String fromAction, 102 @Nullable String outcome, @Nullable NavigationHandler originalNavigationHandler); 103 104 105 /** 106 * Method to be called by subclasses when intending to delegate to the next 107 * handler in the NavigationHandler chain. Will always call the most 108 * appropriate next handler, either the decorated NavigationHandler passed 109 * in as constructor argument or the original NavigationHandler as passed 110 * into this method - according to the position of this instance in the chain. 111 * <p>Will call the decorated NavigationHandler specified as constructor 112 * argument, if any. In case of a DecoratingNavigationHandler as target, the 113 * original NavigationHandler as passed into this method will be passed on to 114 * the next element in the chain: This ensures propagation of the original 115 * handler that the last element in the handler chain might delegate back to. 116 * In case of a standard NavigationHandler as target, the original handler 117 * will simply not get passed on; no delegating back to the original is 118 * possible further down the chain in that scenario. 119 * <p>If no decorated NavigationHandler specified as constructor argument, 120 * this instance is the last element in the chain. Hence, this method will 121 * call the original NavigationHandler as passed into this method. If no 122 * original NavigationHandler has been passed in (for example if this 123 * instance is the last element in a chain with standard NavigationHandlers 124 * as earlier elements), this method corresponds to a no-op. 125 * @param facesContext the current JSF context 126 * @param fromAction the action binding expression that was evaluated to retrieve the 127 * specified outcome, or {@code null} if the outcome was acquired by some other means 128 * @param outcome the logical outcome returned by a previous invoked application action 129 * (which may be {@code null}) 130 * @param originalNavigationHandler the original NavigationHandler, 131 * or {@code null} if none 132 */ 133 protected final void callNextHandlerInChain(FacesContext facesContext, @Nullable String fromAction, 134 @Nullable String outcome, @Nullable NavigationHandler originalNavigationHandler) { 135 136 NavigationHandler decoratedNavigationHandler = getDecoratedNavigationHandler(); 137 138 if (decoratedNavigationHandler instanceof DecoratingNavigationHandler) { 139 // DecoratingNavigationHandler specified through constructor argument: 140 // Call it with original NavigationHandler passed in. 141 DecoratingNavigationHandler decHandler = (DecoratingNavigationHandler) decoratedNavigationHandler; 142 decHandler.handleNavigation(facesContext, fromAction, outcome, originalNavigationHandler); 143 } 144 else if (decoratedNavigationHandler != null) { 145 // Standard NavigationHandler specified through constructor argument: 146 // Call it through standard API, without original NavigationHandler passed in. 147 // The called handler will not be able to redirect to the original handler. 148 decoratedNavigationHandler.handleNavigation(facesContext, fromAction, outcome); 149 } 150 else if (originalNavigationHandler != null) { 151 // No NavigationHandler specified through constructor argument: 152 // Call original handler, marking the end of this chain. 153 originalNavigationHandler.handleNavigation(facesContext, fromAction, outcome); 154 } 155 } 156 157}