View Javadoc

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