1 /* 2 * $Id$ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 package org.apache.struts.action; 22 23 import java.io.Serializable; 24 import java.lang.reflect.InvocationTargetException; 25 import java.util.HashMap; 26 27 import org.apache.commons.beanutils.DynaBean; 28 import org.apache.commons.beanutils.DynaClass; 29 import org.apache.commons.beanutils.DynaProperty; 30 import org.apache.struts.config.FormBeanConfig; 31 import org.apache.struts.config.FormPropertyConfig; 32 import org.apache.struts.util.RequestUtils; 33 34 /** 35 * <p>Implementation of <code>DynaClass</code> for <code>DynaActionForm</code> 36 * classes that allow developers to define ActionForms without having to 37 * individually code all of the classes. <strong>NOTE</strong> - This class is 38 * only used in the internal implementation of dynamic action form beans. 39 * Application developers never need to consult this documentation.</p> 40 * 41 * @version $Rev$ $Date: 2006-01-17 07:26:20 -0500 (Tue, 17 Jan 2006) 42 * $ 43 * @since Struts 1.1 44 */ 45 public class DynaActionFormClass implements DynaClass, Serializable { 46 private static final long serialVersionUID = -2521119905360425245L; 47 48 // ----------------------------------------------------- Instance Variables 49 50 /** 51 * <p>The <code>DynaActionForm</code> implementation <code>Class</code> 52 * which we will use to create new bean instances.</p> 53 */ 54 protected transient Class<? extends DynaActionForm> beanClass = null; 55 56 /** 57 * <p>The form bean configuration information for this class.</p> 58 */ 59 protected FormBeanConfig config = null; 60 61 /** 62 * <p>The "dynamic class name" for this <code>DynaClass</code>.</p> 63 */ 64 protected String name = null; 65 66 /** 67 * <p>The set of dynamic properties that are part of this DynaClass.</p> 68 */ 69 protected DynaProperty[] properties = null; 70 71 /** 72 * <p>The set of dynamic properties that are part of this 73 * <code>DynaClass</code>, keyed by the property name. Individual 74 * descriptor instances will be the same instances as those in the 75 * <code>properties</code> list. 76 */ 77 protected HashMap<String, DynaProperty> propertiesMap = new HashMap<>(); 78 79 // ----------------------------------------------------------- Constructors 80 81 /** 82 * <p>Construct a new <code>DynaActionFormClass</code> for the specified 83 * form bean configuration. This constructor is private; 84 * <code>DynaActionFormClass</code> instances will be created as needed 85 * via calls to the static <code>createDynaActionFormClass()</code> 86 * method.</p> 87 * 88 * @param config The FormBeanConfig instance describing the properties of 89 * the bean to be created 90 * @throws IllegalArgumentException if the bean implementation class 91 * specified in the configuration is not 92 * DynaActionForm (or a subclass of 93 * DynaActionForm) 94 */ 95 public DynaActionFormClass(FormBeanConfig config) { 96 introspect(config); 97 } 98 99 // ------------------------------------------------------ DynaClass Methods 100 101 /** 102 * <p>Return the name of this <code>DynaClass</code> (analogous to the 103 * <code>getName()</code> method of <code>java.lang.Class</code>, which 104 * allows the same <code>DynaClass</code> implementation class to support 105 * different dynamic classes, with different sets of properties. 106 * 107 * @return The name of this <code>DynaClass</code>. 108 */ 109 public String getName() { 110 return (this.name); 111 } 112 113 /** 114 * <p>Return a property descriptor for the specified property, if it 115 * exists; otherwise, return <code>null</code>.</p> 116 * 117 * @param name Name of the dynamic property for which a descriptor is 118 * requested 119 * @return A property descriptor for the specified property. 120 * @throws IllegalArgumentException if no property name is specified 121 */ 122 public DynaProperty getDynaProperty(String name) { 123 if (name == null) { 124 throw new IllegalArgumentException("No property name specified"); 125 } 126 127 return (propertiesMap.get(name)); 128 } 129 130 /** 131 * <p>Return an array of <code>DynaProperty</code>s for the properties 132 * currently defined in this <code>DynaClass</code>. If no properties are 133 * defined, a zero-length array will be returned.</p> 134 * 135 * @return An array of property instances for this class. 136 */ 137 public DynaProperty[] getDynaProperties() { 138 return (properties); 139 140 // :FIXME: Should we really be implementing 141 // getBeanInfo instead, which returns property descriptors 142 // and a bunch of other stuff? 143 } 144 145 /** 146 * <p>Instantiate and return a new {@link DynaActionForm} instance, 147 * associated with this <code>DynaActionFormClass</code>. The properties 148 * of the returned {@link DynaActionForm} will have been initialized to 149 * the default values specified in the form bean configuration 150 * information.</p> 151 * 152 * @return A new {@link DynaActionForm} instance. 153 * @throws IllegalAccessException if the Class or the appropriate 154 * constructor is not accessible 155 * @throws InstantiationException if this Class represents an abstract 156 * class, an array class, a primitive type, 157 * or void; or if instantiation fails for 158 * some other reason 159 */ 160 public DynaBean newInstance() 161 throws IllegalAccessException, InstantiationException { 162 163 DynaActionForm dynaBean; 164 try { 165 dynaBean = getBeanClass().getDeclaredConstructor().newInstance(); 166 } catch (IllegalArgumentException | InvocationTargetException 167 | NoSuchMethodException | SecurityException e) { 168 InstantiationException e2 = new InstantiationException( 169 "Error creating DynaActionForm instance of type " 170 + getBeanClass()); 171 e2.initCause(e); 172 throw e2; 173 } 174 175 dynaBean.setDynaActionFormClass(this); 176 177 FormPropertyConfig[] props = config.findFormPropertyConfigs(); 178 179 for (int i = 0; i < props.length; i++) { 180 dynaBean.set(props[i].getName(), props[i].initial()); 181 } 182 183 return (dynaBean); 184 } 185 186 // --------------------------------------------------------- Public Methods 187 188 /** 189 * <p>Render a <code>String</code> representation of this object.</p> 190 * 191 * @return The string representation of this instance. 192 */ 193 public String toString() { 194 StringBuilder sb = new StringBuilder("DynaActionFormBean[name="); 195 196 sb.append(name); 197 198 DynaProperty[] props = getDynaProperties(); 199 200 if (props == null) { 201 props = new DynaProperty[0]; 202 } 203 204 for (int i = 0; i < props.length; i++) { 205 sb.append(','); 206 sb.append(props[i].getName()); 207 sb.append('/'); 208 sb.append(props[i].getType()); 209 } 210 211 sb.append("]"); 212 213 return (sb.toString()); 214 } 215 216 // --------------------------------------------------------- Static Methods 217 218 /** 219 * Return the <code>DynaActionFormClass</code> instance for the specified 220 * form bean configuration instance. 221 * 222 * @param config The config for which the class should be created. 223 * @return The instance for the specified form bean config. 224 */ 225 public static DynaActionFormClass createDynaActionFormClass( 226 FormBeanConfig config) { 227 return config.getDynaActionFormClass(); 228 } 229 230 // ------------------------------------------------------ Protected Methods 231 232 /** 233 * <p>Return the implementation class we are using to construct new 234 * instances, re-introspecting our {@link FormBeanConfig} if necessary 235 * (that is, after being deserialized, since <code>beanClass</code> is 236 * marked transient).</p> 237 * 238 * @return The implementation class used to construct new instances. 239 */ 240 protected Class<? extends DynaActionForm> getBeanClass() { 241 if (beanClass == null) { 242 introspect(config); 243 } 244 245 return (beanClass); 246 } 247 248 /** 249 * <p>Introspect our form bean configuration to identify the supported 250 * properties.</p> 251 * 252 * @param config The FormBeanConfig instance describing the properties of 253 * the bean to be created 254 * @throws IllegalArgumentException if the bean implementation class 255 * specified in the configuration is not 256 * DynaActionForm (or a subclass of 257 * DynaActionForm) 258 */ 259 protected void introspect(FormBeanConfig config) { 260 this.config = config; 261 262 // Validate the ActionFormBean implementation class 263 final Class<?> clazz; 264 try { 265 clazz = RequestUtils.applicationClass(config.getType()); 266 } catch (Throwable t) { 267 IllegalArgumentException t2 = new IllegalArgumentException( 268 "Cannot instantiate ActionFormBean class '" + config.getType() 269 + "'"); 270 t2.initCause(t); 271 throw t2; 272 } 273 274 if (!DynaActionForm.class.isAssignableFrom(clazz)) { 275 throw new IllegalArgumentException("Class '" + config.getType() 276 + "' is not a subclass of " 277 + "'org.apache.struts.action.DynaActionForm'"); 278 } 279 beanClass = clazz.asSubclass(DynaActionForm.class); 280 281 // Set the name we will know ourselves by from the form bean name 282 this.name = config.getName(); 283 284 // Look up the property descriptors for this bean class 285 FormPropertyConfig[] descriptors = config.findFormPropertyConfigs(); 286 287 if (descriptors == null) { 288 descriptors = new FormPropertyConfig[0]; 289 } 290 291 // Create corresponding dynamic property definitions 292 properties = new DynaProperty[descriptors.length]; 293 294 for (int i = 0; i < descriptors.length; i++) { 295 properties[i] = 296 new DynaProperty(descriptors[i].getName(), 297 descriptors[i].getTypeClass()); 298 propertiesMap.put(properties[i].getName(), properties[i]); 299 } 300 } 301 302 // -------------------------------------------------------- Private Methods 303 }