4

I was reading the JSF implementation <h:form> rendering. To my surprise, I see that (in Mojarra, MyFaces + Tomahawk), they add a hidden input field on an encodeBegin() method.

Here is the sample code for FormRenderer in Mojarra:

@Override
public void encodeBegin(FacesContext context, UIComponent component)
      throws IOException {

    rendererParamsNotNull(context, component);

    if (!shouldEncode(component)) {
        return;
    }

    ResponseWriter writer = context.getResponseWriter();
    assert(writer != null);
    String clientId = component.getClientId(context);
    // since method and action are rendered here they are not added
    // to the pass through attributes in Util class.
    writer.write('\n');
    writer.startElement("form", component);
    writer.writeAttribute("id", clientId, "clientId");
    writer.writeAttribute("name", clientId, "name");
    writer.writeAttribute("method", "post", null);
    writer.writeAttribute("action", getActionStr(context), null);
    String styleClass =
          (String) component.getAttributes().get("styleClass");
    if (styleClass != null) {
        writer.writeAttribute("class", styleClass, "styleClass");
    }
    String acceptcharset = (String)
          component.getAttributes().get("acceptcharset");
    if (acceptcharset != null) {
        writer.writeAttribute("accept-charset", acceptcharset,
                              "acceptcharset");
    }

    RenderKitUtils.renderPassThruAttributes(context,
                                            writer,
                                            component,
                                            ATTRIBUTES);
    writer.writeText("\n", component, null);

    // this hidden field will be checked in the decode method to
    // determine if this form has been submitted.         
    writer.startElement("input", component);
    writer.writeAttribute("type", "hidden", "type");
    writer.writeAttribute("name", clientId,
                          "clientId");
    writer.writeAttribute("value", clientId, "value");
    writer.endElement("input");
    writer.write('\n');

    // Write out special hhidden field for partial submits
    String viewId = context.getViewRoot().getViewId();
    String actionURL =
        context.getApplication().getViewHandler().getActionURL(context, viewId);
    ExternalContext externalContext = context.getExternalContext();
    String encodedActionURL = externalContext.encodeActionURL(actionURL);
    String encodedPartialActionURL = externalContext.encodePartialActionURL(actionURL);
    if (encodedPartialActionURL != null) {
        if (!encodedPartialActionURL.equals(encodedActionURL)) {
            writer.startElement("input", component);
            writer.writeAttribute("type", "hidden", "type");
            writer.writeAttribute("name", "javax.faces.encodedURL", null);
            writer.writeAttribute("value", encodedPartialActionURL, "value");
            writer.endElement("input");
            writer.write('\n');
        }
    }

    if (!writeStateAtEnd) {
        context.getApplication().getViewHandler().writeState(context);
        writer.write('\n');
    }
}

My Questions:

  1. Why is there a hidden input field that gets assigned the id component.getClientId(context), i.e., a UIForm component? What's the purpose of the hidden field?
  2. In Mojarra, you don't have the option to assign your own id attribute on <h:form> but you can on MyFaces. How does JSF treat UIForm in each cases (e.g. when MyFaces has its an explicit provided id)?
  3. Mojarra form enctype is defaulted to application/x-www-form-urlencoded. Can it support multipart/form-data or text/plain?

Thanks

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228

1 Answers1

8

Why is there a hidden input field that gets assigned the id component.getClientId(context), i.e., a UIForm component? What's the purpose of the hidden field?

Look closer at the comment (line numbers are from Mojarra 2.1.19):

153        // this hidden field will be checked in the decode method to
154        // determine if this form has been submitted.         
155        writer.startElement("input", component);
156        writer.writeAttribute("type", "hidden", "type");
157        writer.writeAttribute("name", clientId,
158                              "clientId");
159        writer.writeAttribute("value", clientId, "value");
160        writer.endElement("input");
161        writer.write('\n');

This is thus been used to determine which form was been submitted. This determination happens in turn in the FormRenderer#decode() which is invoked when JSF needs to determine the HTTP request parameters during apply request values phase:

96         // Was our form the one that was submitted?  If so, we need to set
97         // the indicator accordingly..
98         Map<String, String> requestParameterMap = context.getExternalContext()
99               .getRequestParameterMap();
100        if (requestParameterMap.containsKey(clientId)) {
101            if (logger.isLoggable(Level.FINE)) {
102                logger.log(Level.FINE,
103                           "UIForm with client ID {0}, submitted",
104                           clientId);
105            }
106            ((UIForm) component).setSubmitted(true);
107        } else {
108            ((UIForm) component).setSubmitted(false);
109        }

In other words, it controls the outcome of UIForm#isSubmitted() property. It may be useful in the following use cases involving multiple forms in a page:


In Mojarra, you don't have the option to assign your own id attribute on <h:form> but you can on MyFaces. How does JSF treat UIForm in each cases (e.g. when MyFaces has its an explicit provided id)?

I'm not sure what you're talking about. This,

<h:form id="formId">

works fine for me in Mojarra as well.


Mojarra form enctype is defaulted to application/x-www-form-urlencoded. Can it support multipart/form-data or text/plain?

This is as per JSF specification. And yes, you can just use enctype attribute the usual way.

<h:form enctype="multipart/form-data">

Whether the submit of such a form is properly recognized by JSF, is a second. Until JSF 2.2, JSF doesn't have builtin support for multipart/form-data. JSF component libraries solve this by supplying a servlet filter along with their file upload components, usually using Apache Commons FileUpload under the covers. JSF 2.2 just delegates straight to new Servlet 3.0 HttpServletRequest#getPart() API without the need for a servlet filter using a 3rd party API. See also among others How to upload files to server using JSP/Servlet?

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks BalusC! Just out of curiousity, JSF2.2 relies strongly on Servlet 3 Specification? What happens when it is run on Servlet 2.5 and lower (especially for file upload? – Buhake Sindi Jul 09 '13 at 22:33
  • Sorry to ask this, but I need to understand: If you add your own `id` to the `UIForm`, how does JSF know that the form has been submitted? – Buhake Sindi Jul 09 '13 at 22:42
  • Yes, JSF 2.2 is the first version to require Servlet 3.0. See also the [spec](http://jcp.org/aboutJava/communityprocess/final/jsr344/index.html). That's also exactly the reason why it's the first release which offers a file upload component out the standard component set (the ``). Before Servlet 3.0 it was not possible without falling back to a 3rd party API. As to the form ID, that's done in exactly those code snippets in my answer. What exactly don't you understand from those lines? – BalusC Jul 10 '13 at 00:23
  • Sorry about the 2nd question. I asked it in the wee hours of the morning. I appreciate your answer. – Buhake Sindi Jul 10 '13 at 05:13