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.beans.factory.BeanFactory;
023import org.springframework.web.context.WebApplicationContext;
024
025/**
026 * JSF NavigationHandler implementation that delegates to a NavigationHandler
027 * bean obtained from the Spring root WebApplicationContext.
028 *
029 * <p>Configure this handler proxy in your {@code faces-config.xml} file
030 * as follows:
031 *
032 * <pre class="code">
033 * &lt;application&gt;
034 *   ...
035 *   &lt;navigation-handler&gt;
036 *         org.springframework.web.jsf.DelegatingNavigationHandlerProxy
037 *   &lt;/navigation-handler&gt;
038 *   ...
039 * &lt;/application&gt;</pre>
040 *
041 * By default, the Spring ApplicationContext will be searched for the NavigationHandler
042 * under the bean name "jsfNavigationHandler". In the simplest case, this is a plain
043 * Spring bean definition like the following. However, all of Spring's bean configuration
044 * power can be applied to such a bean, in particular all flavors of dependency injection.
045 *
046 * <pre class="code">
047 * &lt;bean name="jsfNavigationHandler" class="mypackage.MyNavigationHandler"&gt;
048 *   &lt;property name="myProperty" ref="myOtherBean"/&gt;
049 * &lt;/bean&gt;</pre>
050 *
051 * The target NavigationHandler bean will typically extend the standard JSF
052 * NavigationHandler class. However, note that decorating the original
053 * NavigationHandler (the JSF provider's default handler) is <i>not</i> supported
054 * in such a scenario, since we can't inject the original handler in standard
055 * JSF style (that is, as constructor argument).
056 *
057 * <p>For <b>decorating the original NavigationHandler</b>, make sure that your
058 * target bean extends Spring's <b>DecoratingNavigationHandler</b> class. This
059 * allows to pass in the original handler as method argument, which this proxy
060 * automatically detects. Note that a DecoratingNavigationHandler subclass
061 * will still work as standard JSF NavigationHandler as well!
062 *
063 * <p>This proxy may be subclassed to change the bean name used to search for the
064 * navigation handler, change the strategy used to obtain the target handler,
065 * or change the strategy used to access the ApplicationContext (normally obtained
066 * via {@link FacesContextUtils#getWebApplicationContext(FacesContext)}).
067 *
068 * @author Juergen Hoeller
069 * @author Colin Sampaleanu
070 * @since 1.2.7
071 * @see DecoratingNavigationHandler
072 */
073public class DelegatingNavigationHandlerProxy extends NavigationHandler {
074
075        /**
076         * Default name of the target bean in the Spring application context:
077         * "jsfNavigationHandler"
078         */
079        public final static String DEFAULT_TARGET_BEAN_NAME = "jsfNavigationHandler";
080
081        private NavigationHandler originalNavigationHandler;
082
083
084        /**
085         * Create a new DelegatingNavigationHandlerProxy.
086         */
087        public DelegatingNavigationHandlerProxy() {
088        }
089
090        /**
091         * Create a new DelegatingNavigationHandlerProxy.
092         * @param originalNavigationHandler the original NavigationHandler
093         */
094        public DelegatingNavigationHandlerProxy(NavigationHandler originalNavigationHandler) {
095                this.originalNavigationHandler = originalNavigationHandler;
096        }
097
098
099        /**
100         * Handle the navigation request implied by the specified parameters,
101         * through delegating to the target bean in the Spring application context.
102         * <p>The target bean needs to extend the JSF NavigationHandler class.
103         * If it extends Spring's DecoratingNavigationHandler, the overloaded
104         * {@code handleNavigation} method with the original NavigationHandler
105         * as argument will be used. Else, the standard {@code handleNavigation}
106         * method will be called.
107         */
108        @Override
109        public void handleNavigation(FacesContext facesContext, String fromAction, String outcome) {
110                NavigationHandler handler = getDelegate(facesContext);
111                if (handler instanceof DecoratingNavigationHandler) {
112                        ((DecoratingNavigationHandler) handler).handleNavigation(
113                                        facesContext, fromAction, outcome, this.originalNavigationHandler);
114                }
115                else {
116                        handler.handleNavigation(facesContext, fromAction, outcome);
117                }
118        }
119
120        /**
121         * Return the target NavigationHandler to delegate to.
122         * <p>By default, a bean with the name "jsfNavigationHandler" is obtained
123         * from the Spring root WebApplicationContext, for every invocation.
124         * @param facesContext the current JSF context
125         * @return the target NavigationHandler to delegate to
126         * @see #getTargetBeanName
127         * @see #getBeanFactory
128         */
129        protected NavigationHandler getDelegate(FacesContext facesContext) {
130                String targetBeanName = getTargetBeanName(facesContext);
131                return getBeanFactory(facesContext).getBean(targetBeanName, NavigationHandler.class);
132        }
133
134        /**
135         * Return the name of the target NavigationHandler bean in the BeanFactory.
136         * Default is "jsfNavigationHandler".
137         * @param facesContext the current JSF context
138         * @return the name of the target bean
139         */
140        protected String getTargetBeanName(FacesContext facesContext) {
141                return DEFAULT_TARGET_BEAN_NAME;
142        }
143
144        /**
145         * Retrieve the Spring BeanFactory to delegate bean name resolution to.
146         * <p>Default implementation delegates to {@code getWebApplicationContext}.
147         * Can be overridden to provide an arbitrary BeanFactory reference to resolve
148         * against; usually, this will be a full Spring ApplicationContext.
149         * @param facesContext the current JSF context
150         * @return the Spring BeanFactory (never {@code null})
151         * @see #getWebApplicationContext
152         */
153        protected BeanFactory getBeanFactory(FacesContext facesContext) {
154                return getWebApplicationContext(facesContext);
155        }
156
157        /**
158         * Retrieve the web application context to delegate bean name resolution to.
159         * <p>Default implementation delegates to FacesContextUtils.
160         * @param facesContext the current JSF context
161         * @return the Spring web application context (never {@code null})
162         * @see FacesContextUtils#getRequiredWebApplicationContext
163         */
164        protected WebApplicationContext getWebApplicationContext(FacesContext facesContext) {
165                return FacesContextUtils.getRequiredWebApplicationContext(facesContext);
166        }
167
168}