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