Primefaces fileUpload not work, FileUploadFilter call twice

4.1k views Asked by At

I'm doing a Web application using Spring 3.1.0.RELEASE, JSF 2.x, JPA 2 with Hibernate Provider. The application run on Tomcat 6.35.

I use PrettyFaces 3.3.2 for friendly URL and Spring security 3.1.0.RELEASE.

I use Primefaces 3.1 and I try to use the fileupload component of Primefaces. Sadly its doesn't work.

I have the good dependencies in my pom.xml

<dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.1</version>
        </dependency><dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.4</version>
        </dependency>

My web.xml :

<?xml version="1.0" encoding="UTF-8"?>
<!-- Use this definition if using a Java EE 6 container This also stops Eclipse
    from complaining that 3.0 is not a valid version <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
    http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <!-- Theme pour prime -->
    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>glass-x</param-value>
    </context-param>

    <!-- The definition of the Root Spring Container shared by all Servlets
        and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/applicationContext.xml
            /WEB-INF/spring/security-app-context.xml
        </param-value>
    </context-param>

    <context-param>
        <param-name>javax.faces.DEVELOPMENT</param-name>
        <param-value>false</param-value>
    </context-param>

    <context-param>
        <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
        <param-value>true</param-value>
    </context-param>


    <!-- jboss-el -->
    <context-param>
        <param-name>com.sun.faces.expressionFactory</param-name>
        <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
    </context-param>

    <filter>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
        <init-param>
            <param-name>thresholdSize</param-name>
            <param-value>51200</param-value>
        </init-param>

        <init-param>
            <param-name>uploadDirectory</param-name>
            <param-value>/tmp/primefacesFileupload</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <url-pattern>/*</url-pattern>
        <servlet-name>Faces Servlet</servlet-name>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ASYNC</dispatcher>
    </filter-mapping>

<!--    Filter for setting all the requests and responses to UTF-8 encoding-->
    <filter>
        <filter-name>spring-encoding-filter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>spring-encoding-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- Localisation -->
    <filter>
        <filter-name>localizationFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>localizationFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- Spring security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- Pretty faces -->
    <filter>
        <filter-name>Pretty Filter</filter-name>
        <filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Pretty Filter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>


    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- place constraints on a single user's ability to log in to your application -->
    <listener>
        <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/app/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/spring/</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>Resource Servlet</servlet-name>
        <servlet-class>org.primefaces.resource.ResourceServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Resource Servlet</servlet-name>
        <url-pattern>/primefaces_resource/*</url-pattern>
    </servlet-mapping>

    <resource-ref>
        <res-ref-name>jsf/ProjectStage</res-ref-name>
        <res-type>java.lang.String</res-type>
        <jndi-name>javax.faces.PROJECT_STAGE</jndi-name>
    </resource-ref>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

In the view I try the following:

<h:form enctype="multipart/form-data" id="testFileupload">
            <pou:fieldset legend="Upload de fichiers">
                <pou:messages showDetail="true"/>

                <pou:fileUpload id="fileUploadInputTest" fileUploadListener="#{siteFormManager.handleFileUpload}" auto="true" dragDropSupport="false"/>
            </pou:fieldset>
        </h:form>

Or

<h:form enctype="multipart/form-data">
    <pou:messages showDetail="true"/>

    <pou:fileUpload value="#{siteFormManager.file}" mode="simple"/>

    <pou:commandButton value="Submit" ajax="false" actionListener="#{siteFormManager.upload}" action="#{siteFormManager.uploadAction}"/>
</h:form>

In my bean

@Component
@Scope(value = "request")
public class SiteFormManager {

    private static final Logger logger = LoggerFactory.getLogger(SiteFormManager.class);

    /**
     * Le file to upload
     */
    private UploadedFile file;

    public void handleFileUpload(FileUploadEvent event) {
        final UploadedFile lfile = event.getFile();
        logger.debug("SiteFormManager::handleFileUpload {} --> File equals {}", event, lfile);
    }

    public void handleFileUpload() {
        logger.debug("SiteFormManager::handleFileUpload without params");
    }

    public String uploadAction() {
        logger.debug("SiteFormManager::uploadAction --> File equals {}", file);
        return null;
    }

    public UploadedFile getFile() {
        return file;
    }

    public void setFile(UploadedFile file) {
        this.file = file;
    }
}

The problem is that the file in my bean is always null! I have try in Tomcat 7 and nothing change. I have try the fileUpload component of Richfaces and it's work, but I wont include the two components libraries to avoid conflicts.

I have debug my application on Netbeans 7 and insert a breakpoint on FileUploadFilter::doFilter. I have notice that the doFilter is called twice. The first time the MultipartRequest is well created, parsing the inputs of the form and detect the file. But the second time the request seems to be consume and nothing is fill in the MultipartRequest. The Primefaces FileUploadRenderer use the second MultipartRequest and so no file is present in that one.

Is it normal that the FileUploadFilter is called twice ? How make the fileUpload component work ?

SOLUTION

I have only change the web.xml, added the ASYNC in the prettyfaces filter and remove all the dispatchers in fileupload filter.

The final web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Use this definition if using a Java EE 6 container This also stops Eclipse
    from complaining that 3.0 is not a valid version <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
    http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <!-- Theme pour prime -->
    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>glass-x</param-value>
    </context-param>

    <!-- The definition of the Root Spring Container shared by all Servlets
        and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/applicationContext.xml
            /WEB-INF/spring/security-app-context.xml
        </param-value>
    </context-param>

    <context-param>
        <param-name>javax.faces.DEVELOPMENT</param-name>
        <param-value>false</param-value>
    </context-param>

    <context-param>
        <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
        <param-value>true</param-value>
    </context-param>


    <!-- jboss-el -->
    <context-param>
        <param-name>com.sun.faces.expressionFactory</param-name>
        <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
    </context-param>

    <filter>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
        <init-param>
            <param-name>thresholdSize</param-name>
            <param-value>51200</param-value>
        </init-param>

        <init-param>
            <param-name>uploadDirectory</param-name>
            <param-value>/tmp/primefacesFileupload</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <url-pattern>/*</url-pattern>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>

<!--    Filter for setting all the requests and responses to UTF-8 encoding-->
    <filter>
        <filter-name>spring-encoding-filter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>spring-encoding-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- Localisation -->
    <filter>
        <filter-name>localizationFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>localizationFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- Spring security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!-- Pretty faces -->
    <filter>
        <filter-name>Pretty Filter</filter-name>
        <filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Pretty Filter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ERROR</dispatcher>
        <dispatcher>ASYNC</dispatcher>
    </filter-mapping>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- place constraints on a single user's ability to log in to your application -->
    <listener>
        <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/app/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/spring/</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>Resource Servlet</servlet-name>
        <servlet-class>org.primefaces.resource.ResourceServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Resource Servlet</servlet-name>
        <url-pattern>/primefaces_resource/*</url-pattern>
    </servlet-mapping>

    <resource-ref>
        <res-ref-name>jsf/ProjectStage</res-ref-name>
        <res-type>java.lang.String</res-type>
        <jndi-name>javax.faces.PROJECT_STAGE</jndi-name>
    </resource-ref>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

Thanks you.

1

There are 1 answers

1
Lincoln On BEST ANSWER

I believe you need to add the ASYNC dispatcher to your PrettyFaces filter mapping:

<!-- Pretty faces -->
<filter>
    <filter-name>Pretty Filter</filter-name>
    <filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>Pretty Filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>

The PrimeFaces upload component uses ASYNC uploads if I am not mistaken.