001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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 * http://www.opensource.org/licenses/ecl2.php 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 */ 016package org.kuali.rice.krad.web.bind; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 020import org.kuali.rice.krad.uif.UifParameters; 021import org.kuali.rice.krad.uif.view.View; 022import org.kuali.rice.krad.uif.UifConstants.ViewType; 023import org.kuali.rice.krad.uif.service.ViewService; 024import org.kuali.rice.krad.util.KRADUtils; 025import org.kuali.rice.krad.web.form.UifFormBase; 026import org.springframework.core.convert.ConversionService; 027import org.springframework.util.Assert; 028import org.springframework.validation.AbstractPropertyBindingResult; 029import org.springframework.web.bind.ServletRequestDataBinder; 030 031import javax.servlet.ServletRequest; 032import javax.servlet.http.HttpServletRequest; 033import java.util.Map; 034 035/** 036 * Override of ServletRequestDataBinder in order to hook in the UifBeanPropertyBindingResult 037 * which instantiates a custom BeanWrapperImpl, and to initialize the view 038 * 039 * @author Kuali Rice Team (rice.collab@kuali.org) 040 */ 041public class UifServletRequestDataBinder extends ServletRequestDataBinder { 042 protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger( 043 UifServletRequestDataBinder.class); 044 045 private UifBeanPropertyBindingResult bindingResult; 046 private ConversionService conversionService; 047 048 public UifServletRequestDataBinder(Object target) { 049 super(target); 050 051 setBindingErrorProcessor(new UifBindingErrorProcessor()); 052 } 053 054 public UifServletRequestDataBinder(Object target, String name) { 055 super(target, name); 056 057 setBindingErrorProcessor(new UifBindingErrorProcessor()); 058 } 059 060 /** 061 * Allows for a custom binding result class. 062 * 063 * @see org.springframework.validation.DataBinder#initBeanPropertyAccess() 064 */ 065 @Override 066 public void initBeanPropertyAccess() { 067 Assert.state(this.bindingResult == null, 068 "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods"); 069 070 this.bindingResult = new UifBeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(), 071 getAutoGrowCollectionLimit()); 072 073 if (this.conversionService != null) { 074 this.bindingResult.initConversion(this.conversionService); 075 } 076 } 077 078 /** 079 * Allows for the setting attributes to use to find the data dictionary data from Kuali 080 * 081 * @see org.springframework.validation.DataBinder#getInternalBindingResult() 082 */ 083 @Override 084 protected AbstractPropertyBindingResult getInternalBindingResult() { 085 if (this.bindingResult == null) { 086 initBeanPropertyAccess(); 087 } 088 089 return this.bindingResult; 090 } 091 092 /** 093 * Disallows direct field access for Kuali 094 * 095 * @see org.springframework.validation.DataBinder#initDirectFieldAccess() 096 */ 097 @Override 098 public void initDirectFieldAccess() { 099 LOG.error("Direct Field access is not allowed in UifServletRequestDataBinder."); 100 throw new RuntimeException("Direct Field access is not allowed in Kuali"); 101 } 102 103 /** 104 * Performs data binding from servlet request parameters to the form, initializes view object, then calls 105 * {@link org.kuali.rice.krad.web.form.UifFormBase#postBind(javax.servlet.http.HttpServletRequest)} 106 * 107 * <p> 108 * The view is initialized by first looking for the {@code viewId} parameter in the request. If found, the view is 109 * retrieved based on this id. If the id is not present, then an attempt is made to find a view by type. In order 110 * to retrieve a view based on type, the view request parameter {@code viewTypeName} must be present. If all else 111 * fails and the viewId is populated on the form (could be populated from a previous request), this is used to 112 * retrieve the view. 113 * </p> 114 * 115 * <p> 116 * Note the view is not initialized for Ajax requests that perform partial page updates or dialog updates or no 117 * updates at all 118 * </p> 119 * 120 * @param request - HTTP Servlet Request instance 121 */ 122 @Override 123 public void bind(ServletRequest request) { 124 super.bind(request); 125 126 UifFormBase form = (UifFormBase) this.getTarget(); 127 128 // if doing a partial page update or ajax request with no updating, do not initialize view 129 if (!form.isUpdateComponentRequest() && !form.isUpdateNoneRequest() && !form.isUpdateDialogRequest()) { 130 View view = null; 131 132 // attempt to retrieve a view by unique identifier first, either as request attribute or parameter 133 String viewId = (String) request.getAttribute(UifParameters.VIEW_ID); 134 if (StringUtils.isBlank(viewId)) { 135 viewId = request.getParameter(UifParameters.VIEW_ID); 136 } 137 138 if (StringUtils.isNotBlank(viewId)) { 139 view = getViewService().getViewById(viewId); 140 } 141 142 // attempt to get view instance by type parameters 143 if (view == null) { 144 view = getViewByType(request, form); 145 } 146 147 // if view not found attempt to find one based on the cached form 148 if (view == null) { 149 view = getViewFromPreviousModel(form); 150 151 if (view != null) { 152 LOG.warn("Obtained viewId from cached form, this may not be safe!"); 153 } 154 } 155 156 if (view != null) { 157 form.setViewId(view.getId()); 158 159 160 } else { 161 form.setViewId(null); 162 } 163 164 form.setView(view); 165 } 166 167 // invoke form callback for custom binding 168 form.postBind((HttpServletRequest) request); 169 } 170 171 /** 172 * Attempts to get a view instance by looking for a view type name in the request or the form and querying 173 * that view type with the request parameters 174 * 175 * @param request request instance to pull parameters from 176 * @param form form instance to pull values from 177 * @return View instance if found or null 178 */ 179 protected View getViewByType(ServletRequest request, UifFormBase form) { 180 View view = null; 181 182 String viewTypeName = request.getParameter(UifParameters.VIEW_TYPE_NAME); 183 ViewType viewType = StringUtils.isBlank(viewTypeName) ? form.getViewTypeName() : ViewType.valueOf(viewTypeName); 184 185 if (viewType != null) { 186 Map<String, String> parameterMap = KRADUtils.translateRequestParameterMap(request.getParameterMap()); 187 view = getViewService().getViewByType(viewType, parameterMap); 188 } 189 190 return view; 191 } 192 193 /** 194 * Attempts to get a view instance based on the view id stored on the form (which might not be populated 195 * from the request but remaining from session) 196 * 197 * @param form form instance to pull view id from 198 * @return View instance associated with form's view id or null if id or view not found 199 */ 200 protected View getViewFromPreviousModel(UifFormBase form) { 201 // maybe we have a view id from the session form 202 if (form.getViewId() != null) { 203 return getViewService().getViewById(form.getViewId()); 204 } 205 206 return null; 207 } 208 209 public ViewService getViewService() { 210 return KRADServiceLocatorWeb.getViewService(); 211 } 212 213} 214 215