1 /*
2 * $Id: FormBeanConfig.java 54929 2004-10-16 16:38:42Z germuska $
3 *
4 * Copyright 1999-2004 The Apache Software Foundation.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19
20 package org.apache.struts.config;
21
22
23 import java.io.Serializable;
24 import java.util.HashMap;
25 import org.apache.commons.beanutils.DynaBean;
26 import org.apache.commons.beanutils.MutableDynaClass;
27 import org.apache.struts.action.DynaActionForm;
28 import org.apache.struts.action.DynaActionFormClass;
29 import org.apache.struts.action.ActionServlet;
30 import org.apache.struts.action.ActionForm;
31 import org.apache.struts.validator.BeanValidatorForm;
32
33
34 /**
35 * <p>A JavaBean representing the configuration information of a
36 * <code><form-bean></code> element in a Struts
37 * configuration file.<p>
38 *
39 * @version $Rev: 54929 $ $Date: 2004-10-16 17:38:42 +0100 (Sat, 16 Oct 2004) $
40 * @since Struts 1.1
41 */
42
43 public class FormBeanConfig implements Serializable {
44
45
46 // ----------------------------------------------------- Instance Variables
47
48
49 /**
50 * Has this component been completely configured?
51 */
52 protected boolean configured = false;
53
54
55 /**
56 * The set of FormProperty elements defining dynamic form properties for
57 * this form bean, keyed by property name.
58 */
59 protected HashMap formProperties = new HashMap();
60
61
62 /**
63 * <p>The lockable object we can synchronize on when creating DynaActionFormClass.</p>
64 */
65 protected String lock = "";
66
67
68 // ------------------------------------------------------------- Properties
69
70
71 /**
72 * The DynaActionFormClass associated with a DynaActionForm.
73 */
74 protected transient DynaActionFormClass dynaActionFormClass;
75
76 /**
77 * <p>Return the DynaActionFormClass associated with a DynaActionForm.</p>
78 *
79 * @exception IllegalArgumentException if the ActionForm is not dynamic
80 */
81 public DynaActionFormClass getDynaActionFormClass() {
82
83 if (dynamic == false) {
84 throw new IllegalArgumentException("ActionForm is not dynamic");
85 }
86 synchronized (lock) {
87 if (dynaActionFormClass == null) {
88 dynaActionFormClass = new DynaActionFormClass(this);
89 }
90 }
91 return dynaActionFormClass;
92 }
93
94
95 /**
96 * Is the form bean class an instance of DynaActionForm with dynamic
97 * properties?
98 */
99 protected boolean dynamic = false;
100
101 public boolean getDynamic() {
102 return (this.dynamic);
103 }
104
105 /**
106 * @deprecated The value to be returned by <code>getDynamic()</code>
107 * is now computed automatically in <code>setType()</code>
108 */
109 public void setDynamic(boolean dynamic) {
110 if (configured) {
111 throw new IllegalStateException("Configuration is frozen");
112 }
113 ; // No action required
114 }
115
116 /**
117 * The unique identifier of this form bean, which is used to reference this
118 * bean in <code>ActionMapping</code> instances as well as for the name of
119 * the request or session attribute under which the corresponding form bean
120 * instance is created or accessed.
121 */
122 protected String name = null;
123
124 public String getName() {
125 return (this.name);
126 }
127
128 public void setName(String name) {
129 if (configured) {
130 throw new IllegalStateException("Configuration is frozen");
131 }
132 this.name = name;
133 }
134
135
136 /**
137 * The fully qualified Java class name of the implementation class
138 * to be used or generated.
139 */
140 protected String type = null;
141
142 public String getType() {
143 return (this.type);
144 }
145
146 public void setType(String type) {
147 if (configured) {
148 throw new IllegalStateException("Configuration is frozen");
149 }
150 this.type = type;
151 Class dynaBeanClass = DynaActionForm.class;
152 Class formBeanClass = formBeanClass();
153 if (formBeanClass != null) {
154 if (dynaBeanClass.isAssignableFrom(formBeanClass)) {
155 this.dynamic = true;
156 } else {
157 this.dynamic = false;
158 }
159 } else {
160 this.dynamic = false;
161 }
162 }
163
164 /**
165 * Is this DynaClass currently restricted (for DynaBeans with a MutableDynaClass).
166 */
167 protected boolean restricted = false;
168
169 /**
170 * <p>Indicates whether a MutableDynaClass is currently restricted.</p>
171 * <p>If so, no changes to the existing registration of property names,
172 * data types, readability, or writeability are allowed.</p>
173 */
174 public boolean isRestricted() {
175 return restricted;
176 }
177
178 /**
179 * <p>Set whether a MutableDynaClass is currently restricted.</p>
180 * <p>If so, no changes to the existing registration of property names,
181 * data types, readability, or writeability are allowed.</p>
182 */
183 public void setRestricted(boolean restricted) {
184 this.restricted = restricted;
185 }
186
187
188 // --------------------------------------------------------- Public Methods
189
190
191 /**
192 * <p>Create and return an <code>ActionForm</code> instance appropriate
193 * to the information in this <code>FormBeanConfig</code>.</p>
194 *
195 * @param servlet The action servlet
196 * @return ActionForm instance
197 * @exception IllegalAccessException if the Class or the appropriate
198 * constructor is not accessible
199 * @exception InstantiationException if this Class represents an abstract
200 * class, an array class, a primitive type, or void; or if instantiation
201 * fails for some other reason
202 */
203 public ActionForm createActionForm(ActionServlet servlet)
204 throws IllegalAccessException, InstantiationException {
205
206 Object obj = null;
207
208 // Create a new form bean instance
209 if (getDynamic()) {
210 obj = getDynaActionFormClass().newInstance();
211 } else {
212 obj = formBeanClass().newInstance();
213 }
214
215 ActionForm form = null;
216 if (obj instanceof ActionForm) {
217 form = (ActionForm)obj;
218 } else {
219 form = new BeanValidatorForm(obj);
220 }
221
222 form.setServlet(servlet);
223
224 if (form instanceof DynaBean &&
225 ((DynaBean)form).getDynaClass() instanceof MutableDynaClass) {
226 DynaBean dynaBean = (DynaBean)form;
227 MutableDynaClass dynaClass = (MutableDynaClass)dynaBean.getDynaClass();
228
229 // Add properties
230 dynaClass.setRestricted(false);
231 FormPropertyConfig props[] = findFormPropertyConfigs();
232 for (int i = 0; i < props.length; i++) {
233 dynaClass.add(props[i].getName(), props[i].getTypeClass());
234 dynaBean.set(props[i].getName(), props[i].initial());
235 }
236 dynaClass.setRestricted(isRestricted());
237
238 }
239
240 return form;
241
242 }
243
244
245 /**
246 * Add a new <code>FormPropertyConfig</code> instance to the set associated
247 * with this module.
248 *
249 * @param config The new configuration instance to be added
250 *
251 * @exception IllegalArgumentException if this property name has already
252 * been defined
253 */
254 public void addFormPropertyConfig(FormPropertyConfig config) {
255
256 if (configured) {
257 throw new IllegalStateException("Configuration is frozen");
258 }
259 if (formProperties.containsKey(config.getName())) {
260 throw new IllegalArgumentException("Property " +
261 config.getName() +
262 " already defined");
263 }
264 formProperties.put(config.getName(), config);
265
266 }
267
268
269 /**
270 * Return the form property configuration for the specified property
271 * name, if any; otherwise return <code>null</code>.
272 *
273 * @param name Form property name to find a configuration for
274 */
275 public FormPropertyConfig findFormPropertyConfig(String name) {
276
277 return ((FormPropertyConfig) formProperties.get(name));
278
279 }
280
281
282 /**
283 * Return the form property configurations for this module. If there
284 * are none, a zero-length array is returned.
285 */
286 public FormPropertyConfig[] findFormPropertyConfigs() {
287
288 FormPropertyConfig results[] =
289 new FormPropertyConfig[formProperties.size()];
290 return ((FormPropertyConfig[]) formProperties.values().toArray(results));
291
292 }
293
294
295 /**
296 * Freeze the configuration of this component.
297 */
298 public void freeze() {
299
300 configured = true;
301
302 FormPropertyConfig[] fpconfigs = findFormPropertyConfigs();
303 for (int i = 0; i < fpconfigs.length; i++) {
304 fpconfigs[i].freeze();
305 }
306
307 }
308
309
310 /**
311 * Remove the specified form property configuration instance.
312 *
313 * @param config FormPropertyConfig instance to be removed
314 */
315 public void removeFormPropertyConfig(FormPropertyConfig config) {
316
317 if (configured) {
318 throw new IllegalStateException("Configuration is frozen");
319 }
320 formProperties.remove(config.getName());
321
322 }
323
324
325 /**
326 * Return a String representation of this object.
327 */
328 public String toString() {
329
330 StringBuffer sb = new StringBuffer("FormBeanConfig[");
331 sb.append("name=");
332 sb.append(this.name);
333 sb.append(",type=");
334 sb.append(this.type);
335 sb.append("]");
336 return (sb.toString());
337
338 }
339
340
341 // ------------------------------------------------------ Protected Methods
342
343
344 /**
345 * Return the <code>Class</code> instance for the form bean implementation
346 * configured by this <code>FormBeanConfig</code> instance. This method
347 * uses the same algorithm as <code>RequestUtils.applicationClass()</code>
348 * but is reproduced to avoid a runtime dependence.
349 */
350 protected Class formBeanClass() {
351
352 ClassLoader classLoader =
353 Thread.currentThread().getContextClassLoader();
354 if (classLoader == null) {
355 classLoader = this.getClass().getClassLoader();
356 }
357 try {
358 return (classLoader.loadClass(getType()));
359 } catch (Exception e) {
360 return (null);
361 }
362
363 }
364
365
366 }