javaexceptionseam2seam-conversation

Maintain Conversation After Exception (Handled in Pages.xml)


I'm working in a project that I didn't originally start, so there are a lot of artifacts and conventions that I cannot change without considerable effort. Anyway, here's the problem. I need to do multiple file uploads (that are "children" to the entity that is being edited on the "main" parent page) that get cached on the server so that they can be sent somewhere else if/when the user submits. The files uploaded also include metadata that the user enters. The best way I have figured to do this is to render a "dialog" that has an iframe that does the uploading and has the input for the metadata. Then I created an override (using install precedence) of Seam's multipart filter that uses the Apache file upload jar and a custom request wrapper that carries my information into the action call on the server. All goes well unless I throw an exception out of the filter, for example, if the request size is too big. The exception is caught and handled by a pages.xml declaration.

<exception class="org.jboss.seam.web.FileUploadException">
     <redirect view-id="#{facesContext.externalContext.requestServletPath}">
          <message severity='ERROR'>#{org.jboss.seam.handledException.message}</message> 
     </redirect>
</exception>

When I normally submit the form in my dialog frame, the conversation in the frame remains (as I want), when the exception is caught, I get a new one (as I don't want). I want the error messages passed by the exception shown in the global messages area of the frame, but I need to remain in the same conversation as before since the "children" that are being added in the dialog are children to the "parent" entity in the main page. Here is the form inside the frame code. I have tried s:button (does not submit the form), tried parameters, and tried hidden inputs with the conversation id.

<h:form id="attachmentModalForm" enctype="multipart/form-data" autocomplete="off" style="background-color: #FFFFFF;">
     <ui:include src="layout/messages.xhtml" />
     <div id="attachmentModalMain" class="modalMain">
          <s:decorate id="attachmentDescriptionDecoration" template="layout/edit.xhtml" styleClass="twoCol">
               <ui:define name="label">#{messages['contents.attachmentDialog.label.description']}<s:span styleClass="required">*</s:span></ui:define>
               <h:inputText id="attachmentDescription" value="#{attachmentAction.description}" styleClass="textbox" />
          </s:decorate>
          <s:decorate id="attachmentFileDecoration" template="layout/edit.xhtml" styleClass="twoCol">
               <ui:define name="label">#{messages['contents.attachmentDialog.label.file']}<s:span styleClass="required">*</s:span></ui:define>
               <input id="attachmentFile" name="attachmentFile" type="file" />
          </s:decorate>
     </div>
     <div id="attachmentModalSubmit" class="modalSubmit">
          <h:commandButton id="attachmentSubmitButton" value="#{messages['action.text.submit']}" action="#{attachmentAction.addAttachment()}" onclick="attachmentSubmit();" />
          <button id="attachmentCancelButton" type="button" value="#{messages['action.text.cancel']}" onclick="window.parent.hideAttachmentModal();">#{messages['action.text.cancel']}</button>
     </div>
     <input type="hidden" name="cid" value="#{conversation.id}" />
</h:form>

Here is the filter that overrides Seam's multipart filter.

package XXXXXXXXXXXXX.attachment;  

import java.io.File;  
import java.io.IOException;  
import java.rmi.server.UID;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  

import javax.servlet.FilterChain;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  

import org.apache.commons.fileupload.FileItem;  
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;  
import org.apache.commons.fileupload.FileUploadException;  
import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
import org.apache.commons.fileupload.servlet.ServletFileUpload;  
import org.jboss.seam.ScopeType;  
import org.jboss.seam.annotations.Install;  
import org.jboss.seam.annotations.Name;  
import org.jboss.seam.annotations.Scope;  
import org.jboss.seam.annotations.intercept.BypassInterceptors;  
import org.jboss.seam.annotations.web.Filter;  
import org.jboss.seam.web.AbstractFilter;  

/** 
* This filter is used to override Seam's multipart filter so that we 
* can have multiple temporary files on the server cued and ready to 
* go to XXXXXXXXX. It uses the Apache Commons FileUpload objects to 
* handle the parsing of the request and the temporary files. 
* 
*/  
@Scope(ScopeType.APPLICATION)  
@Name("org.jboss.seam.web.multipartFilter")  
@Install(precedence = Install.APPLICATION)  
@BypassInterceptors  
@Filter(within={"org.jboss.seam.web.ajax4jsfFilter", "org.jboss.seam.web.exceptionFilter"})  
public class MEDWareMultipartFilter extends AbstractFilter {  

    // This is unused, we always want temp files since we are caching before upload to XXXXXXXXX.  
    // Leaving it in to mirror Seam's multipart filter, in case it gets set from the components.xml.  
    @SuppressWarnings("unused") private boolean createTempFiles = true;  
    private int maxRequestSize = -1;  
    private String acceptedFileExtensions = "txt,pdf,doc,docx,xls,xlsx";  

    public void setCreateTempFiles(boolean createTempFiles) { }  

    public void setMaxRequestSize(int maxFileSize) {  
        this.maxRequestSize = maxFileSize;  
    }  

    public String getAcceptedFileExtensions() {  
        return acceptedFileExtensions;  
    }  

    public void setAcceptedFileExtensions(String acceptedFileExtensions) {  
        this.acceptedFileExtensions = acceptedFileExtensions;  
    }  

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  

        if (!(response instanceof HttpServletResponse)) {  
            chain.doFilter(request, response);  
            return;  
        }  

        HttpServletRequest httpRequest = (HttpServletRequest) request;  

        if (ServletFileUpload.isMultipartContent(httpRequest)) {  
            File repository = (File) this.getServletContext().getAttribute("javax.servlet.context.tempdir");  
            DiskFileItemFactory factory = new DiskFileItemFactory(0, repository);  
            ServletFileUpload upload = new ServletFileUpload(factory);  
            upload.setSizeMax(maxRequestSize);  

            List<FileItem> formItems = null;  

            try {  
                formItems = upload.parseRequest(httpRequest);  
            } catch (SizeLimitExceededException slee) {  
                throw new org.jboss.seam.web.FileUploadException("File size excededs maximum allowed.", slee);  
            } catch (FileUploadException fue) {  
                throw new org.jboss.seam.web.FileUploadException("Error uploading file.", fue);  
            }  

            Map<String, String> parameters = new HashMap<String, String>();  
            Map<String, File> fileParameters = new HashMap<String, File>();  

            if (formItems != null && formItems.size() > 0) {  
                for (FileItem item : formItems) {  
                    if (item.isFormField()) {  
                        parameters.put(item.getFieldName(), item.getString());  
                    } else {  
                        String fileName = item.getName();  

                        // This is for IE7 (and Safari?) which sends the whole path.  
                        fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);  

                        if (!MyMultipartRequestUtils.isValidFileType(acceptedFileExtensions, fileName)) {  
                            throw new org.jboss.seam.web.FileUploadException("The file type is not an accepted file type.");  
                        }  

                        File tempFile = null;  
                        try {  
                            tempFile = File.createTempFile(new UID().toString().replace(":", "-"), ".upload");  
                            tempFile.deleteOnExit();  
                            item.write(tempFile);  
                        } catch (Exception e) {  
                            throw new org.jboss.seam.web.FileUploadException("Error uploading file. Could not write file to server.");  
                        }  
                        fileParameters.put(fileName, tempFile);  
                    }  
                }  
            }  
            MyMultipartRequestWrapper requestWrapper = new MyMultipartRequestWrapper(httpRequest, parameters, fileParameters);  
            chain.doFilter(requestWrapper, response);  
        } else {  
            chain.doFilter(request, response);  
        }  
    }  

}  

So, to reiterate, if I use the frame to upload files, everything works and the frame continues on the same conversation adding each child to the parent, if I upload a file that is too big, I get the correct message in the global message area of the frame, but the conversation get incremented and then the children are obviously being added to a new parent entity in the new conversation. Any help would be greatly appreciated.


Solution

  • I just ended up adding a field to my custom request wrapper to carry any exceptions I wanted to handle and then handled them in the

    #{attachmentAction.addAttachment()}
    

    action call further up the request chain. All works now, I just add my faces messages from there and the conversation does not get incremented the same as if I had a successful upload.