001/* 002 * Copyright 2002-2017 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.el; 018 019import java.beans.FeatureDescriptor; 020import java.util.Iterator; 021 022import javax.el.ELContext; 023import javax.el.ELException; 024import javax.el.ELResolver; 025import javax.el.PropertyNotWritableException; 026import javax.faces.context.FacesContext; 027 028import org.springframework.lang.Nullable; 029import org.springframework.web.context.WebApplicationContext; 030import org.springframework.web.jsf.FacesContextUtils; 031 032/** 033 * JSF {@code ELResolver} that delegates to the Spring root {@code WebApplicationContext}, 034 * resolving name references to Spring-defined beans. 035 * 036 * <p>Configure this resolver in your {@code faces-config.xml} file as follows: 037 * 038 * <pre class="code"> 039 * <application> 040 * ... 041 * <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> 042 * </application></pre> 043 * 044 * All your JSF expressions can then implicitly refer to the names of 045 * Spring-managed service layer beans, for example in property values of 046 * JSF-managed beans: 047 * 048 * <pre class="code"> 049 * <managed-bean> 050 * <managed-bean-name>myJsfManagedBean</managed-bean-name> 051 * <managed-bean-class>example.MyJsfManagedBean</managed-bean-class> 052 * <managed-bean-scope>session</managed-bean-scope> 053 * <managed-property> 054 * <property-name>mySpringManagedBusinessObject</property-name> 055 * <value>#{mySpringManagedBusinessObject}</value> 056 * </managed-property> 057 * </managed-bean></pre> 058 * 059 * with "mySpringManagedBusinessObject" defined as Spring bean in 060 * applicationContext.xml: 061 * 062 * <pre class="code"> 063 * <bean id="mySpringManagedBusinessObject" class="example.MySpringManagedBusinessObject"> 064 * ... 065 * </bean></pre> 066 * 067 * @author Juergen Hoeller 068 * @since 2.5 069 * @see WebApplicationContextFacesELResolver 070 * @see org.springframework.web.jsf.FacesContextUtils#getRequiredWebApplicationContext 071 */ 072public class SpringBeanFacesELResolver extends ELResolver { 073 074 @Override 075 @Nullable 076 public Object getValue(ELContext elContext, @Nullable Object base, Object property) throws ELException { 077 if (base == null) { 078 String beanName = property.toString(); 079 WebApplicationContext wac = getWebApplicationContext(elContext); 080 if (wac.containsBean(beanName)) { 081 elContext.setPropertyResolved(true); 082 return wac.getBean(beanName); 083 } 084 } 085 return null; 086 } 087 088 @Override 089 @Nullable 090 public Class<?> getType(ELContext elContext, @Nullable Object base, Object property) throws ELException { 091 if (base == null) { 092 String beanName = property.toString(); 093 WebApplicationContext wac = getWebApplicationContext(elContext); 094 if (wac.containsBean(beanName)) { 095 elContext.setPropertyResolved(true); 096 return wac.getType(beanName); 097 } 098 } 099 return null; 100 } 101 102 @Override 103 public void setValue(ELContext elContext, @Nullable Object base, Object property, Object value) throws ELException { 104 if (base == null) { 105 String beanName = property.toString(); 106 WebApplicationContext wac = getWebApplicationContext(elContext); 107 if (wac.containsBean(beanName)) { 108 if (value == wac.getBean(beanName)) { 109 // Setting the bean reference to the same value is alright - can simply be ignored... 110 elContext.setPropertyResolved(true); 111 } 112 else { 113 throw new PropertyNotWritableException( 114 "Variable '" + beanName + "' refers to a Spring bean which by definition is not writable"); 115 } 116 } 117 } 118 } 119 120 @Override 121 public boolean isReadOnly(ELContext elContext, @Nullable Object base, Object property) throws ELException { 122 if (base == null) { 123 String beanName = property.toString(); 124 WebApplicationContext wac = getWebApplicationContext(elContext); 125 if (wac.containsBean(beanName)) { 126 return true; 127 } 128 } 129 return false; 130 } 131 132 @Override 133 @Nullable 134 public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, @Nullable Object base) { 135 return null; 136 } 137 138 @Override 139 public Class<?> getCommonPropertyType(ELContext elContext, @Nullable Object base) { 140 return Object.class; 141 } 142 143 /** 144 * Retrieve the web application context to delegate bean name resolution to. 145 * <p>The default implementation delegates to FacesContextUtils. 146 * @param elContext the current JSF ELContext 147 * @return the Spring web application context (never {@code null}) 148 * @see org.springframework.web.jsf.FacesContextUtils#getRequiredWebApplicationContext 149 */ 150 protected WebApplicationContext getWebApplicationContext(ELContext elContext) { 151 FacesContext facesContext = FacesContext.getCurrentInstance(); 152 return FacesContextUtils.getRequiredWebApplicationContext(facesContext); 153 } 154 155}