001/** 002 * Copyright 2005-2018 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.form; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.CoreApiServiceLocator; 020import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 021import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 022import org.kuali.rice.krad.uif.util.SessionTransient; 023 024import java.io.Serializable; 025import java.lang.annotation.Annotation; 026import java.lang.reflect.Field; 027import java.util.ArrayList; 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033import java.util.Vector; 034 035/** 036 * Manages Uif form objects for a session. 037 * 038 * @author Kuali Rice Team (rice.collab@kuali.org) 039 */ 040public class UifFormManager implements Serializable { 041 private static final long serialVersionUID = -6323378881342207080L; 042 043 private int maxNumberOfSessionForms = 5; 044 045 protected Vector accessedFormKeys; 046 047 protected Map<String, UifFormBase> sessionForms; 048 049 /** 050 * Create a new form manager with an empty list of forms for the session. 051 */ 052 public UifFormManager() { 053 this.accessedFormKeys = new Vector(); 054 this.sessionForms = new HashMap<String, UifFormBase>(); 055 056 if (CoreApiServiceLocator.getKualiConfigurationService() != null) { 057 String maxNumberOfSessionFormsConfig = 058 CoreApiServiceLocator.getKualiConfigurationService().getPropertyValueAsString( 059 "maxNumberOfSessionForms"); 060 if (StringUtils.isNotBlank(maxNumberOfSessionFormsConfig)) { 061 maxNumberOfSessionForms = Integer.parseInt(maxNumberOfSessionFormsConfig); 062 } 063 } 064 } 065 066 /** 067 * Add a form to the session. 068 * 069 * @param form to be added to the session 070 */ 071 public synchronized void addSessionForm(UifFormBase form) { 072 if (form == null || StringUtils.isBlank(form.getFormKey())) { 073 throw new RiceIllegalArgumentException("Form or form key was null"); 074 } 075 076 sessionForms.put(form.getFormKey(), form); 077 078 // add form key to top of vector indicating it is most recent 079 if (accessedFormKeys.contains(form.getFormKey())) { 080 accessedFormKeys.removeElement(form.getFormKey()); 081 } 082 accessedFormKeys.add(form.getFormKey()); 083 084 // check if we have too many forms and need to remove an old one 085 if (sessionForms.size() > maxNumberOfSessionForms) { 086 // get the oldest form we have 087 String removeFormKey = (String) accessedFormKeys.get(0); 088 if (sessionForms.containsKey(removeFormKey)) { 089 sessionForms.remove(removeFormKey); 090 } 091 accessedFormKeys.removeElementAt(0); 092 } 093 } 094 095 /** 096 * Retrieve a form from the session. 097 * 098 * @param formKey of the form to retrieve from the session 099 * @return UifFormBase 100 */ 101 public UifFormBase getSessionForm(String formKey) { 102 if (sessionForms.containsKey(formKey)) { 103 return sessionForms.get(formKey); 104 } 105 106 return null; 107 } 108 109 /** 110 * Removes the stored form data and the forms from the breadcrumb history from the session. 111 * 112 * @param form to be removed 113 */ 114 public void removeSessionForm(UifFormBase form) { 115 if (form == null || StringUtils.isBlank(form.getFormKey())) { 116 return; 117 } 118 119 removeSessionFormByKey(form.getFormKey()); 120 } 121 122 /** 123 * Removes the stored form data and the forms from the breadcrumb history from the session. 124 * 125 * @param formKey of the form to be removed 126 */ 127 public void removeFormWithHistoryFormsByKey(String formKey) { 128 if (sessionForms.containsKey(formKey)) { 129 removeSessionFormByKey(formKey); 130 } 131 } 132 133 /** 134 * Removes the stored form data from the session. 135 * 136 * @param formKey of the form to be removed 137 */ 138 public void removeSessionFormByKey(String formKey) { 139 if (accessedFormKeys.contains(formKey)) { 140 accessedFormKeys.removeElement(formKey); 141 } 142 143 if (sessionForms.containsKey(formKey)) { 144 sessionForms.remove(formKey); 145 } 146 } 147 148 /** 149 * Indicates whether the form manager has a session form with the given key. 150 * 151 * @param formKey key of the form in session to check for 152 * @return true if the manager contains the session form, false if not 153 */ 154 public boolean hasSessionForm(String formKey) { 155 return sessionForms.containsKey(formKey); 156 } 157 158 /** 159 * Retrieves the session form based on the formkey and updates the non session transient 160 * variables on the request form from the session form. 161 * 162 * @param requestForm 163 * @param formKey 164 */ 165 public void updateFormWithSession(UifFormBase requestForm, String formKey) { 166 UifFormBase sessionForm = sessionForms.get(formKey); 167 if (sessionForm == null) { 168 return; 169 } 170 171 if (!sessionForm.getClass().isAssignableFrom(requestForm.getClass())) { 172 throw new RuntimeException( 173 "Session form mismatch, session form class not assignable from request form class"); 174 } 175 176 List<Field> fields = new ArrayList<Field>(); 177 fields = getAllFields(fields, sessionForm.getClass(), UifFormBase.class); 178 for (Field field : fields) { 179 boolean copyValue = true; 180 for (Annotation an : field.getAnnotations()) { 181 if (an instanceof SessionTransient) { 182 copyValue = false; 183 } 184 } 185 186 if (copyValue && ObjectPropertyUtils.isReadableProperty(sessionForm, field.getName()) && ObjectPropertyUtils 187 .isWritableProperty(sessionForm, field.getName())) { 188 Object fieldValue = ObjectPropertyUtils.getPropertyValue(sessionForm, field.getName()); 189 ObjectPropertyUtils.setPropertyValue(requestForm, field.getName(), fieldValue); 190 } 191 } 192 } 193 194 /** 195 * Removes the values that are marked @SessionTransient from the form. 196 * 197 * @param form the form from which the session transient values have been purged 198 */ 199 public void purgeForm(UifFormBase form) { 200 List<Field> fields = new ArrayList<Field>(); 201 fields = getAllFields(fields, form.getClass(), UifFormBase.class); 202 for (Field field : fields) { 203 boolean purgeValue = false; 204 205 if (!field.getType().isPrimitive()) { 206 for (Annotation an : field.getAnnotations()) { 207 if (an instanceof SessionTransient) { 208 purgeValue = true; 209 } 210 } 211 } 212 213 if (purgeValue && ObjectPropertyUtils.isWritableProperty(form, field.getName())) { 214 ObjectPropertyUtils.setPropertyValue(form, field.getName(), null); 215 } 216 } 217 } 218 219 private List<Field> getAllFields(List<Field> fields, Class<?> type, Class<?> stopAt) { 220 for (Field field : type.getDeclaredFields()) { 221 fields.add(field); 222 } 223 224 if (type.getSuperclass() != null && !type.getName().equals(stopAt.getName())) { 225 fields = getAllFields(fields, type.getSuperclass(), stopAt); 226 } 227 228 return fields; 229 } 230 231 /** 232 * Internal vector maintained to keep track of accessed form and the order in which they were accessed. 233 * 234 * <p>Used for the form clearing process. When forms are added to the manager their key is added to the top of 235 * the vector. When a form needs to be cleared, the form identified by the key at the botton of this vector 236 * is removed</p> 237 * 238 * @return Vector instance holding form key strings 239 */ 240 protected Vector getAccessedFormKeys() { 241 return accessedFormKeys; 242 } 243 244 /** 245 * Maximum number of forms that can be stored at one time by the manager. 246 * 247 * @return int max number of forms 248 */ 249 public int getMaxNumberOfSessionForms() { 250 return maxNumberOfSessionForms; 251 } 252 253 /** 254 * @see UifFormManager#getMaxNumberOfSessionForms() 255 */ 256 public void setMaxNumberOfSessionForms(int maxNumberOfSessionForms) { 257 this.maxNumberOfSessionForms = maxNumberOfSessionForms; 258 } 259 260}