Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Glassfish 4, JSF 2.2 and PrimeFaces FileUploadEvent not working together

After upgrading to GlassFish 4 and JSF 2.2 Primefaces FileUploadEvent stop working. With JSF 2.1 it was working with no problem. Everything is working fine except file uploading. Is there something that I am missing?

    GlassFish 4
    JSF 2.2
    PrimeFaces 3.4.2 and 3.5
    Commons io version: 2.4
    Commons fileupload version: 1.3

Controller side

public void handleFileUpload(FileUploadEvent event) {
    System.out.println("HandleFileUpload");
    byte[] file = event.getFile().getContents();
    newFieldset.setData(file);
    FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
    FacesContext.getCurrentInstance().addMessage(null, msg);
}

View

<h:form enctype="multipart/form-data">
            <p:fieldset legend="Create new feed" toggleable="true" collapsed="true" >
                <p:fileUpload fileUploadListener="#{adminHomeController.handleFileUpload}" style="margin-top: 20px;"
                              mode="advanced" 
                              update="messages"
                              sizeLimit="1000000" 
                              multiple="false" 
                              allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>
                <p:inputText label="Baslik" style="margin-top: 20px;" required="true" value="#{adminHomeController.newFieldset.legend}"  /> 
                <p:editor style="margin-top: 20px;"
                          value="#{adminHomeController.newFieldset.content}" />
                <p:commandButton style="margin-top: 20px;" value="#{msg['common.save']}" update="messages" icon="ui-icon-disk" actionListener="#{adminHomeController.saveFieldset()}"/>
            </p:fieldset>
            <p:growl id="messages" showDetail="true"/>
        </h:form>
like image 556
Pinchy Avatar asked Jun 20 '13 02:06

Pinchy


2 Answers

I was finally able to figure it out. Commons-fileuploads method parseRequest(httpServletRequest) tries to read the request's inputStream. Since the container already read it, it is empty. So what can be done to solve this? The answer is a bit more complicated than I initially thought it would be. First you will need your own FileUploadFilter which could look like this:

public class FileUploadFilter implements Filter 
{
private final static Logger LOGGER = LoggerFactory.getLogger(FileUploadFilter.class);

/*
 * (non-Javadoc)
 * 
 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
 */
@Override
public void init(FilterConfig filterConfig) throws ServletException 
{
}

/*
 * (non-Javadoc)
 * 
 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
 * javax.servlet.ServletResponse, javax.servlet.FilterChain)
 */
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException 
{
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    boolean isMultipart = (httpServletRequest.getContentType() == null) ? false : httpServletRequest.getContentType().toLowerCase().startsWith("multipart/");

    if (isMultipart) 
    {
        MultipartRequest multipartRequest = new MultipartRequest(httpServletRequest);

        LOGGER.info("File upload request parsed succesfully, continuing with filter chain with a wrapped multipart request");

        filterChain.doFilter(multipartRequest, response);
    }
    else 
    {
        filterChain.doFilter(request, response);
    }
}

/*
 * (non-Javadoc)
 * 
 * @see javax.servlet.Filter#destroy()
 */
@Override
public void destroy() 
{
    LOGGER.info("Destroying UploadFilter");
}

Next: Register this filter in your web.xml and remove/replace the Primefaces filter. This should look something like this:

  <filter>
    <filter-name>FileUpload Filter</filter-name>
    <filter-class><YourPackage>.FileUploadFilter</filter-class>
  </filter>
  <filter-mapping>
     <filter-name>FileUpload Filter</filter-name>
     <servlet-name>Faces Servlet</servlet-name>
  </filter-mapping>

Unfortunately thats not it. You will need your own MultipartRequest since you have to assemble the list of FileItems by yourself. But Stop. We have to work with the javax.servlet.Part classes which are not compatible with the FileItem. So i wrote a new class which bridges these two. You can find this class here: http://pastebin.com/JcfAYjey

The last piece of the puzzle is the mentioned MultipartRequest which links the PartItem and the FileUploadFilter. I took this class from the Primefaces-Repository and changed it according to out needs (see http://pastebin.com/Vc5h2rmJ). The difference is between lines 47 and 57.

So what do you have to do: 1. Create the three classes FileUploadFilter, MultipartRequest and PartItem 2. Register the FileUploadFilter in your web.xml 3. Enjoy!

PLEASE NOTE: This is not intended as a solve-all-problems solution but a merely a direction you may take in further implementations. The MultipartRequest for example will only work for parts with content-type image/*. You may need to change this.

Feel free to change the code ;) Hope it helps!

EDIT: I forgot to mention one important step. You will additionally need your Own FileIUploadRenderer. The one Primefaces implemented uses an instanceof check to find the MultipartRequest. Since you are now using a different one the import has to be changed. The rest of the class can stay the same (http://pastebin.com/rDUkPqf6). Don't forget to register it inside of your faces-config.xml :

<render-kit>
   <renderer>
        <component-family>org.primefaces.component</component-family>
        <renderer-type>org.primefaces.component.FileUploadRenderer</renderer-type>
        <renderer-class><YourPackage>.FileUploadRenderer</renderer-class>
     </renderer>
</render-kit>
like image 145
Kai Avatar answered Sep 30 '22 16:09

Kai


Answer lies in UploadFile getInputstream() method. Don't rely on getContents() method. This is my simple solution which worked with the below dependencies in glassfish 4

  • Primefaces 4.0.RC1
  • jsf 2.2
  • commons-fileupload 1.3

    private byte[] getFileContents(InputStream in) {
        byte[] bytes = null;
        try {            
            // write the inputStream to a FileOutputStream            
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int read = 0;
             bytes = new byte[1024];
    
            while ((read = in.read(bytes)) != -1) {
                bos.write(bytes, 0, read);
            }
            bytes = bos.toByteArray();
            in.close();
            in = null;
            bos.flush();
            bos.close();
            bos = null;
            logger.debug("New file created!");
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
        return bytes;
    }
    getFileContents(getFile().getInputstream()); 
    
like image 32
Kenshin Avatar answered Sep 30 '22 16:09

Kenshin