View Javadoc
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  
22  package org.apache.struts.faces.component;
23  
24  
25  import java.util.Map;
26  
27  import org.apache.commons.beanutils.DynaBean;
28  import org.apache.struts.Globals;
29  import org.apache.struts.action.ActionForm;
30  import org.apache.struts.action.ActionServlet;
31  import org.apache.struts.action.DynaActionFormClass;
32  import org.apache.struts.config.ActionConfig;
33  import org.apache.struts.config.FormBeanConfig;
34  import org.apache.struts.config.ModuleConfig;
35  import org.apache.struts.faces.util.StrutsContext;
36  import org.apache.struts.faces.util.Utils;
37  import org.apache.struts.util.RequestUtils;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import jakarta.el.ValueExpression;
42  import jakarta.faces.component.UIForm;
43  import jakarta.faces.context.FacesContext;
44  
45  
46  /**
47   * <strong>FormComponent</strong> is a specialized subclass of
48   * {@code jakarta.faces.component.UIForm} that supports automatic
49   * creation of form beans in request or session scope.
50   *
51   * @version $Rev$ $Date$
52   */
53  public class FormComponent extends UIForm {
54  
55  
56      // -------------------------------------------------------- Static Variables
57  
58  
59      /**
60       * The {@code Log} instance for this class.
61       */
62      private final Logger log =
63          LoggerFactory.getLogger(FormComponent.class);
64  
65  
66      // ------------------------------------------------------ Instance Variables
67  
68  
69      private String action;
70      private String enctype;
71      private String focus;
72      private String focusIndex;
73      private String onreset;
74      private String onsubmit;
75      private String style;
76      private String styleClass;
77      private String target;
78  
79  
80      // ---------------------------------------------------- Component Properties
81  
82  
83      /**
84       * <p>Return the Struts action path to which this form should be submitted.
85       * </p>
86       */
87      public String getAction() {
88  
89          if (this.action != null) {
90              return (this.action);
91          }
92          ValueExpression vb = getValueExpression("action");
93          if (vb != null) {
94              return ((String) vb.getValue(getFacesContext().getELContext()));
95          } else {
96              return (null);
97          }
98  
99      }
100 
101 
102     /**
103      * <p>Set the Struts action to which this form should be submitted.</p>
104      *
105      * @param action The new action path
106      */
107     public void setAction(String action) {
108 
109         this.action = action;
110 
111     }
112 
113 
114     /**
115      * <p>Return the encoding type for this form submit.</p>
116      */
117     public String getEnctype() {
118 
119         if (this.enctype != null) {
120             return (this.enctype);
121         }
122         ValueExpression vb = getValueExpression("enctype");
123         if (vb != null) {
124             return ((String) vb.getValue(getFacesContext().getELContext()));
125         } else {
126             return (null);
127         }
128 
129     }
130 
131 
132     /**
133      * <p>Set the encoding type for this form submit.</p>
134      *
135      * @param enctype The new enctype path
136      */
137     public void setEnctype(String enctype) {
138 
139         this.enctype = enctype;
140 
141     }
142 
143 
144     /**
145      * <p>Return the component family to which this component belongs.</p>
146      */
147     public String getFamily() {
148 
149         return "org.apache.struts.faces.Form";
150 
151     }
152 
153 
154     /**
155      * <p>Return the focus element name.</p>
156      */
157     public String getFocus() {
158 
159         if (this.focus != null) {
160             return (this.focus);
161         }
162         ValueExpression vb = getValueExpression("focus");
163         if (vb != null) {
164             return ((String) vb.getValue(getFacesContext().getELContext()));
165         } else {
166             return (null);
167         }
168 
169     }
170 
171 
172     /**
173      * <p>Set the focus element name.</p>
174      *
175      * @param focus The new focus path
176      */
177     public void setFocus(String focus) {
178 
179         this.focus = focus;
180 
181     }
182 
183 
184     /**
185      * <p>Return the focus element index.</p>
186      */
187     public String getFocusIndex() {
188 
189         if (this.focusIndex != null) {
190             return (this.focusIndex);
191         }
192         ValueExpression vb = getValueExpression("focusIndex");
193         if (vb != null) {
194             return ((String) vb.getValue(getFacesContext().getELContext()));
195         } else {
196             return (null);
197         }
198 
199     }
200 
201 
202     /**
203      * <p>Set the focus element index.</p>
204      *
205      * @param focusIndex The new focusIndex path
206      */
207     public void setFocusIndex(String focusIndex) {
208 
209         this.focusIndex = focusIndex;
210 
211     }
212 
213 
214     /**
215      * <p>Return the JavaScript to execute on form reset.</p>
216      */
217     public String getOnreset() {
218 
219         if (this.onreset != null) {
220             return (this.onreset);
221         }
222         ValueExpression vb = getValueExpression("onreset");
223         if (vb != null) {
224             return ((String) vb.getValue(getFacesContext().getELContext()));
225         } else {
226             return (null);
227         }
228 
229     }
230 
231 
232     /**
233      * <p>Set the JavaScript to execute on form reset.</p>
234      *
235      * @param onreset The new onreset path
236      */
237     public void setOnreset(String onreset) {
238 
239         this.onreset = onreset;
240 
241     }
242 
243 
244     /**
245      * <p>Return the JavaScript to execute on form submit.</p>
246      */
247     public String getOnsubmit() {
248 
249         if (this.onsubmit != null) {
250             return (this.onsubmit);
251         }
252         ValueExpression vb = getValueExpression("onsubmit");
253         if (vb != null) {
254             return ((String) vb.getValue(getFacesContext().getELContext()));
255         } else {
256             return (null);
257         }
258 
259     }
260 
261 
262     /**
263      * <p>Set the JavaScript to execute on form submit.</p>
264      *
265      * @param onsubmit The new onsubmit path
266      */
267     public void setOnsubmit(String onsubmit) {
268 
269         this.onsubmit = onsubmit;
270 
271     }
272 
273 
274     /**
275      * <p>Return the CSS style(s) to be rendered for this component.</p>
276      */
277     public String getStyle() {
278 
279         ValueExpression vb = getValueExpression("style");
280         if (vb != null) {
281             return (String) vb.getValue(getFacesContext().getELContext());
282         } else {
283             return style;
284         }
285 
286     }
287 
288 
289     /**
290      * <p>Set the CSS style(s) to be rendered for this component.</p>
291      *
292      * @param style The new CSS style(s)
293      */
294     public void setStyle(String style) {
295 
296         this.style = style;
297 
298     }
299 
300 
301     /**
302      * <p>Return the CSS style class(es) to be rendered for this component.</p>
303      */
304     public String getStyleClass() {
305 
306         ValueExpression vb = getValueExpression("styleClass");
307         if (vb != null) {
308             return (String) vb.getValue(getFacesContext().getELContext());
309         } else {
310             return styleClass;
311         }
312 
313     }
314 
315 
316     /**
317      * <p>Set the CSS style class(es) to be rendered for this component.</p>
318      *
319      * @param styleClass The new CSS style class(es)
320      */
321     public void setStyleClass(String styleClass) {
322 
323         this.styleClass = styleClass;
324 
325     }
326 
327 
328     /**
329      * <p>Return the target frame for the response to this form submit.</p>
330      */
331     public String getTarget() {
332 
333         ValueExpression vb = getValueExpression("target");
334         if (vb != null) {
335             return (String) vb.getValue(getFacesContext().getELContext());
336         } else {
337             return target;
338         }
339 
340     }
341 
342 
343     /**
344      * <p>Set the target frame for the response to this form submit.</p>
345      *
346      * @param target The new CSS target(s)
347      */
348     public void setTarget(String target) {
349 
350         this.target = target;
351 
352     }
353 
354 
355     // ---------------------------------------------------------- UIForm Methods
356 
357 
358     /**
359      * <p>Create an instance of the form bean (if necessary) before
360      * delegating to the standard decoding process.</p>
361      *
362      * @param context FacesContext for the request we are processing
363      */
364     public void processDecodes(FacesContext context) {
365 
366         if (context == null) {
367             throw new NullPointerException();
368         }
369 
370         String clientId = getClientId(context);
371         log.debug("processDecodes({})", clientId);
372 
373         // Create the form bean (if necessary)
374         Map<String, String> params = context.getExternalContext().getRequestParameterMap();
375         if (params.containsKey(clientId)) {
376             createActionForm(context);
377         }
378 
379         // Perform the standard decode processing
380         super.processDecodes(context);
381 
382     }
383 
384 
385     /**
386      * <p>Restore our state from the specified object.</p>
387      *
388      * @param context <code>FacesContext</code> for the current request
389      * @param state Object containing our saved state
390      */
391     public void restoreState(FacesContext context, Object state) {
392 
393         Object values[] = (Object[]) state;
394         super.restoreState(context, values[0]);
395         action = (String) values[1];
396         enctype = (String) values[2];
397         focus = (String) values[3];
398         focusIndex = (String) values[4];
399         onreset = (String) values[5];
400         onsubmit = (String) values[6];
401         style = (String) values[7];
402         styleClass = (String) values[8];
403         target = (String) values[9];
404 
405     }
406 
407 
408     /**
409      * <p>Create and return an object representing our state to be saved.</p>
410      *
411      * @param context <code>FacesContext</code> for the current request
412      */
413     public Object saveState(FacesContext context) {
414 
415         Object values[] = new Object[10];
416         values[0] = super.saveState(context);
417         values[1] = action;
418         values[2] = enctype;
419         values[3] = focus;
420         values[4] = focusIndex;
421         values[5] = onreset;
422         values[6] = onsubmit;
423         values[7] = style;
424         values[8] = styleClass;
425         values[9] = target;
426         return (values);
427 
428     }
429 
430 
431     // ---------------------------------------------------------- Public Methods
432 
433 
434     /**
435      * <p>Create an appropriate form bean in the appropriate scope, if one
436      * does not already exist.</p>
437      *
438      * @param context FacesContext for the current request
439      *
440      * @exception IllegalArgumentException if no ActionConfig for the
441      *  specified action attribute can be located
442      * @exception IllegalArgumentException if no FormBeanConfig for the
443      *  specified form bean can be located
444      * @exception IllegalArgumentException if no ModuleConfig can be
445      *  located for this application module
446      */
447     public void createActionForm(FacesContext context) {
448 
449         final StrutsContext strutsContext = new StrutsContext(context);
450 
451         // Look up the application module configuration information we need
452         ModuleConfig moduleConfig = strutsContext.getModuleConfig();
453 
454         // Look up the ActionConfig we are processing
455         String action = getAction();
456         ActionConfig actionConfig = moduleConfig.findActionConfig(action);
457         if (actionConfig == null) {
458             throw new IllegalArgumentException("Cannot find action '" +
459                                                action + "' configuration");
460         }
461 
462         // Does this ActionConfig specify a form bean?
463         String name = actionConfig.getName();
464         if (name == null) {
465             return;
466         }
467 
468         // Look up the FormBeanConfig we are processing
469         FormBeanConfig fbConfig = moduleConfig.findFormBeanConfig(name);
470         if (fbConfig == null) {
471             throw new IllegalArgumentException("Cannot find form bean '" +
472                                                name + "' configuration");
473         }
474 
475         // Does a usable form bean attribute already exist?
476         String attribute = actionConfig.getAttribute();
477         String scope = actionConfig.getScope();
478         ActionForm instance = null;
479         if ("request".equals(scope)) {
480             instance = Utils.getMapValue(ActionForm.class,
481                     context.getExternalContext().getRequestMap(), attribute);
482         } else if ("session".equals(scope)) {
483             context.getExternalContext().getSession(true);
484             instance = Utils.getMapValue(ActionForm.class,
485                     context.getExternalContext().getSessionMap(), attribute);
486         }
487         if (instance != null) {
488             if (fbConfig.getDynamic()) {
489                 String className =
490                     ((DynaBean) instance).getDynaClass().getName();
491                 if (className.equals(fbConfig.getName())) {
492                     log.debug(" Recycling existing DynaActionForm instance "
493                         + "of type '{}'", className);
494                     return;
495                 }
496             } else {
497                 try {
498                     Class<?> configClass =
499                         RequestUtils.applicationClass(fbConfig.getType());
500                     if (configClass.isAssignableFrom(instance.getClass())) {
501                         log.debug(" Recycling existing ActionForm instance "
502                             + "of class '{}'", instance.getClass().getName());
503                         return;
504                     }
505                 } catch (Throwable t) {
506                     IllegalArgumentException t2 = new IllegalArgumentException
507                         ("Cannot load form bean class '" +
508                          fbConfig.getType() + "'");
509                     t2.initCause(t);
510                     throw t2;
511                 }
512             }
513         }
514 
515         // Create a new form bean instance
516         if (fbConfig.getDynamic()) {
517             try {
518                 DynaActionFormClass dynaClass =
519                     DynaActionFormClass.createDynaActionFormClass(fbConfig);
520                 instance = (ActionForm) dynaClass.newInstance();
521                 log.debug(" Creating new DynaActionForm instance "
522                     + "of type '{}'", fbConfig.getType());
523                 log.trace(" --> {}", instance);
524             } catch (Throwable t) {
525                 IllegalArgumentException t2 = new IllegalArgumentException
526                     ("Cannot create form bean of type '" +
527                      fbConfig.getType() + "'");
528                 t2.initCause(t);
529                 throw t2;
530             }
531         } else {
532             try {
533                 instance = (ActionForm)
534                     RequestUtils.applicationInstance(fbConfig.getType());
535                 log.debug(" Creating new ActionForm instance "
536                     + "of type '{}'", fbConfig.getType());
537                 log.trace(" --> {}", instance);
538             } catch (Throwable t) {
539                 IllegalArgumentException t2 = new IllegalArgumentException
540                     ("Cannot create form bean of class '" +
541                      fbConfig.getType() + "'");
542                 t2.initCause(t);
543                 throw t2;
544             }
545         }
546 
547         // Configure and cache the form bean instance in the correct scope
548         ActionServlet servlet = Utils.getMapValue(ActionServlet.class,
549                 context.getExternalContext().getApplicationMap(),
550                 Globals.ACTION_SERVLET_KEY);
551         instance.setServlet(servlet);
552         if ("request".equals(scope)) {
553             context.getExternalContext().getRequestMap().put
554                 (attribute, instance);
555         } else if ("session".equals(scope)) {
556             context.getExternalContext().getSessionMap().put
557                 (attribute, instance);
558         }
559 
560     }
561 }