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 }