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}