6 Replies Latest reply on Apr 10, 2008 1:44 PM by Randy Saborio

    rich:fileUpload fails with parent dynamically rendered compo

    Randy Saborio Newbie

      The component rich:fileUpload will not fail in the following code:

      <h:form enctype="multipart/form-data" rendered="#{param['someparam'] == 'somevalue'}">
       <rich:fileUpload uploadData="#{bean.files}" fileUploadListener="#{bean.listener}">
       <f:param name="someparam" value="somevalue"/>
       </rich:fileUpload>
      </h:form>


      But it will fail in the following:

      <a4j:outputPanel rendered="#{param['someparam'] == 'somevalue'}">
       <h:form enctype="multipart/form-data">
       <rich:fileUpload uploadData="#{bean.files}" fileUploadListener="#{bean.listener}">
       <f:param name="someparam" value="somevalue"/>
       </rich:fileUpload>
       </h:form>
      </a4j:outputPanel>


      The only difference is that the rendered property of the h:form component is moved out to an external parent component.

      I'm using RichFaces 3.2.0 RC8.

      HELP!!!
      Thanks

        • 1. Re: rich:fileUpload fails with parent dynamically rendered c
          Nick Belaevski Master

          It seems that rich:fileUpload component doesn't handle f:param children. I've filed RFC to JIRA: http://jira.jboss.com/jira/browse/RF-2925

          • 2. Re: rich:fileUpload fails with parent dynamically rendered c
            Randy Saborio Newbie

            Where can I download the Richfaces 3.2.1 Snapshot?

            • 3. Re: rich:fileUpload fails with parent dynamically rendered c
              Ilya Shaikovsky Master

              check out Rich Faces trunk folder from SVN and build it manually as described at our wiki. Need some time to check snapshots creation at jboss.org folders.

              • 4. Re: rich:fileUpload fails with parent dynamically rendered c
                Randy Saborio Newbie

                The problem still persist on 3.2.1. The generated code for ProgressBar of a FileUpload component (JavaScript code) is ignoring the request parameters assigned to its FileUpload. The ProgressBar of FileUpload component MUST send exactly the same request parameters assigned to FileUpload or it will break request driven applications.

                I've try the RF 3.2.1 Snapshot.

                Thanks
                HELP!

                • 5. Re: rich:fileUpload fails with parent dynamically rendered c
                  Andrei Markavtsov Apprentice

                  rHINOx,

                  If f:param defined inside the fileupload component it means that this parameter (name=value) will be sent to server together with file.

                  So you can see effect on server side only after Upload button clicked and file was uploaded.

                  Progress bar does not send this parameter to server during its polling.
                  We think that it should not do this, because progress bar is another independent component and children component defined in file upload should not effect on progress bar.

                  Anyway we cannot understand the purpose of usage f:param inside file upload in your case.

                  Can you please explain this in details?

                  Thanks.

                  • 6. Re: rich:fileUpload fails with parent dynamically rendered c
                    Randy Saborio Newbie

                    The problem is with request driven applications. Suppose the following:

                    users.xhtml
                    <ui:composition>
                     <ui:param name="user" value="#{userController.user}"/>
                    
                     <h:panelGrid id="panel1">
                     <h:outputText value="#{user.username}"/>
                     <h:outputText value="#{user.firstName}"/>
                     <h:outputText value="#{user.lastName}"/>
                     </h:panelGrid>
                    
                    </ui:composition>
                    
                    User.java
                    public class User {
                     private String id;
                     private String username;
                     private String firstName;
                     private String lastName;
                     private Collection<UploadItem> images;
                    
                     public User() {
                     }
                    
                     public String getId() {
                     return id;
                     }
                    
                     public void setId(String id) {
                     this.id = id;
                     }
                    
                     public String getUsername() {
                     return username;
                     }
                    
                     public void setUsername(String username) {
                     this.username = username;
                     }
                    
                     public String getFirstName() {
                     return firstName;
                     }
                    
                     public void setFirstName(String firstName) {
                     this.firstName = firstName;
                     }
                    
                     public String getLastName() {
                     return lastName;
                     }
                    
                     public void setLastName(String lastName) {
                     this.lastName = lastName;
                     }
                    
                     public Collection<UploadItem> getImages() {
                     return images;
                     }
                    
                     public void setImages(Collection<UploadItem> images) {
                     this.images = images;
                     }
                    }
                    
                    UserController.java
                    public class UserController {
                     private User user;
                    
                     public UserController() {
                     initialize();
                     }
                    
                     private void initialize() {
                     String id = ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getParameter("id");
                     UserDAO userDAO = ...;
                     user = userDAO.findById(id);
                     }
                    
                     public User getUser() {
                     return user;
                     }
                    
                     public void listener(UploadEvent event) throws IOException {
                     user.addImage(event.getUploadItem());
                     UserDAO userDAO = ...;
                     userDAO.save(user);
                     }
                    
                     public String someAction() {
                     // perform some action with current user
                     user.setUsername("username changed!");
                     return ""; // means same page
                     }
                    }
                    
                    RequestListener.java
                    public class RequestListener implements PhaseListener {
                     private static boolean isRequestingView(String view) {
                     String path = FacesContext.getCurrentInstance().getExternalContext().getRequestServletPath();
                     return path != null && path.equals(view);
                     }
                    
                     private boolean isValidView() {
                     if (isRequestingView("/users.faces")) {
                     UserDAO userDAO = ...;
                     String id = ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getParameter("id");
                     return id != null && userDAO.findById(id) != null;
                     }
                     return true;
                     }
                    
                     public void afterPhase(PhaseEvent event) {
                     }
                    
                     public void beforePhase(PhaseEvent event) {
                     if (!isValidView()) {
                     FacesContext context = FacesContext.getCurrentInstance();
                     context.responseComplete();
                     context.getApplication().getNavigationHandler().handleNavigation(context, null, "error_404");
                     }
                     }
                    
                     public PhaseId getPhaseId() {
                     return PhaseId.RESTORE_VIEW;
                     }
                    }


                    UserController is declared as request scope in faces-config.xml, RequestListener is declared as lifecycle phase listener in faces-config.xml

                    Now, if I make the following request: http://.../users.faces?id=15 and there is an user with id 15 the page users.xhtml will display the user's information (username, first and last name), but, if there is no user with id = 15 OR there is no request parameter named id at all, RequestListener will send a 404 error and UserController will never be reached. Now lets introduce a4j components on the same page for re-render some area:

                    users.xhtml
                    <ui:composition>
                     <ui:param name="user" value="#{userController.user}"/>
                    
                     <a4j:form>
                     <h:panelGrid id="panel1">
                     <h:outputText value="#{user.username}"/>
                     <h:outputText value="#{user.firstName}"/>
                     <h:outputText value="#{user.lastName}"/>
                     </h:panelGrid>
                    
                     <a4j:commandLink value="Perform Some Action" action="#{userController.someAction}" reRender="panel1">
                     <f:param name="id" value="#{user.id}"/>
                     </a4j:commandLink>
                     </a4j:form>
                    
                    </ui:composition>


                    Take specially care of <f:param name="id" value="#{user.id}"/> inside a4j:commandLink. IF this parameter is not sent with request, the application WILL not work, why? because is a requirement of users.xhtml page according to RequestListener. The parameter is needed for displaying user's information. In this case, everything will work ok, a4j:commandLink makes the request, RequestListener verifies the existence of user with id = 15 and let UserController receive the id to find the user, then someAction method is invoked and user's username will be modified.

                    Now lets introduce rich fileUpload component:

                    users.xhtml
                    <ui:composition>
                     <ui:param name="user" value="#{userController.user}"/>
                    
                     <a4j:form>
                     <h:panelGrid id="panel1">
                     <h:outputText value="#{user.username}"/>
                     <h:outputText value="#{user.firstName}"/>
                     <h:outputText value="#{user.lastName}"/>
                     </h:panelGrid>
                    
                     <rich:fileUpload fileUploadListener="#{userController.listener}">
                     <f:param name="id" value="#{user.id}"/>
                     </rich:fileUpload>
                     </a4j:form>
                    
                    </ui:composition>


                    This code simply will not work. The generated javascript for progress bar ignores the <f:param name="id" value="#{user.id}"/> and because of that, the RequestListener will receive (during progress bar status polling) a NULL id parameter causing a 404 error.

                    Every single request made by fileUpload component's children MUST send exactly the same parameters assigned to it.

                    The workaround is to use "progress facet" of file upload to force progress bar to send the request parameter, but this is a wrong behavior of fileUpload component:

                    users.xhtml
                    <ui:composition>
                     <ui:param name="user" value="#{userController.user}"/>
                    
                     <a4j:form>
                     <h:panelGrid id="panel1">
                     <h:outputText value="#{user.username}"/>
                     <h:outputText value="#{user.firstName}"/>
                     <h:outputText value="#{user.lastName}"/>
                     </h:panelGrid>
                    
                     <rich:fileUpload fileUploadListener="#{userController.listener}">
                     <f:param name="id" value="#{user.id}"/>
                     <f:facet name="progress">
                     <rich:progressBar>
                     <f:param name="id" value="#{user.id}"/>
                     </rich:progressBar>
                     </f:facet>
                     </rich:fileUpload>
                     </a4j:form>
                    
                    </ui:composition>


                    This code WILL work but you cannot consider the fileUpload and its progress bar as separate components, they must be considered as a unique component or at least work as one, and that is why the generated progress bar MUST send all parameters as well assigned to its file upload parent component.

                    I hope this help.
                    Thanks a lot.