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