3

Background:

Using AJAX JQuery form to upload file asynchronously and receive JSON response back from Controller, extract the data and update the view without page refresh.

Tools and Version:

IBM WebSphere Portal Server v8, Spring + Spring MVC Portlet v4.1.5, Commons-fileupload v1.3.1, Jackson-core/databind v2.5.1, json-20131018.jar.

What I've tried before:

Used portlet:resourceURL and handled that in Controller as ResourceRequest and using @ResponseBody which was fine i.e. able to return as JSON response back to the view and extract the data and update the view but the problem with this approach is that the mulitpart file never submitted to the Controller using ResourceRequest. So I had to find a way to get this file.

Later learned that @ResponseBody is still not supported in Spring MVC Portlet per https://jira.spring.io/browse/SPR-7344 and suggested to use ActionRequest instead of ResourceRequest per Handling Multipart request that is not an Action request? and https://www.liferay.com/community/forums/-/message_boards/message/50151319 -

What I am doing now:

When I tried to use ActionRequest as suggested above, the render phase follows the action phase always return as text/html but what i wanted is application/json. So tried custom view resolver as described(pls refer AjaxView.java) below but that doesn't help - still getting text/html page, any help?

JSP:

<TR>
    <TD align="left" id="fileUploadWrapper">
        <form:input id="file" path="msgList[${status.index}].file" type="file"/>                                                                                                                                
        <form:hidden path="msgList[${status.index}].file"/>                                                                 

        <input name="uploadBtn" id="uploadBtn" type="button" value="Attach"  onclick="disabledConfirm_exit=true;uploadJqueryForm()";/>                                                                                                  
    </TD>
</TR>

JS:

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"> </script>
<script src="http://malsup.github.com/jquery.form.js"></script> 

function uploadJqueryForm(){   
   var url = '<portlet:actionURL><portlet:param name="action" value="upload"/></portlet:actionURL>';   
   $("#replyMessageForm").ajaxForm({
   type: "POST",
   url: url,   
    success:function(responce) {                    
           $.each(responce,function(key, attachment) {          

        var htmlrowStart = '<TR name="DataContainer">';         
        var htmlcol1 = '<TD name="Document_PageAutoDelete_ColumnData" valign="top" class="tableCell"><a href=<portlet:actionURL><portlet:param name="action" value="upload"/><portlet:param name="attachmentID" value='+attachment.attachmentID+'/></portlet:actionURL> onclick="disabledConfirm_exit=true; javascript:submitMsgForm(replyMessageForm);">'+"[Remove]"+'</a> </TD>';
        var htmlrowEnd = '</TR>';

        $('#fileListTblID').append(htmlrowStart);
        $('#fileListTblID').append(htmlcol1);       
        $('#fileListTblID').append(htmlrowEnd);          
        });
     },     
   }).submit();   
 }

Controller:

    @ActionMapping(params = "action=upload")
public void onUpload(ActionRequest actionRequest,ActionResponse actionResponse, Model model,@Valid @ModelAttribute("messageForm") MessageForm messageForm, BindingResult result) throws Exception {

    if (result.hasErrors()) {
        actionResponse..setRenderParameter("action","send");
    } else {            
        CommonsMultipartFile multipartFile =  messageForm.getMessageList().get(0).getFile();                                
        String orgName = multipartFile.getOriginalFilename();                       
        Attachment attachment = new Attachment();
        attachment.setAttachmentID((message.getId() != null ? message.getId() + genUniRandInt():genUniRandInt()));
        attachment.setAttachmentName(orgName);          
        attachment.setAttachmentSize(toString(new Long(multipartFile.getSize()/1000).toString()));
        byte[] bytes = multipartFile.getBytes();            
        String encFile = encoder.encode(bytes);
        attachment.setAttachmentData(encFile);                                          
        model.addAttribute("attachment", attachment);               
        actionResponse.setRenderParameter("action","fileUpload");           
    }
}

@RenderMapping(params = "action=fileUpload")
public ModelAndView process(RenderRequest request, Model model) throws IOException {
    Map map = new HashMap();
    Attachment attachment = (Attachment)model.asMap().get("attachment");        
    map.put("attachment", attachment);
    return new ModelAndView("ajaxView", map);
}    

VIEW:

import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;   
@Component("ajaxView")
public class AjaxView extends AbstractView {     
     public AjaxView() {
         super();
         setContentType("application/json");
     }

    @Override
    protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest request, HttpServletResponse response)
            throws Exception {          
        JSONObject jsonObj = new JSONObject(map);       
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(jsonObj.toString());
        response.getWriter().flush();
    }       
}   

applicationContext.xml

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property>
    <property name="prefix"> <value>/WEB-INF/jsp/</value> </property>
    <property name="suffix"> <value>.jsp</value> </property>
    <property name="order"> <value>2</value> </property>
</bean> 
<bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
   <property name="order" value="1" />
</bean>

Error:

Caused by: java.lang.IllegalArgumentException: application/json not valid compared to [text/html]
    at com.ibm.ws.portletcontainer.core.impl.PortletResponseImpl.setContentType(PortletResponseImpl.java:579)
    at com.ibm.ws.portletcontainer.core.impl.RenderResponseImpl.setContentType(RenderResponseImpl.java:113)
    at javax.portlet.filter.RenderResponseWrapper.setContentType(RenderResponseWrapper.java:139)
    at javax.portlet.filter.RenderResponseWrapper.setContentType(RenderResponseWrapper.java:139)
    at org.springframework.web.portlet.DispatcherPortlet.render(DispatcherPortlet.java:1126)
    at org.springframework.web.portlet.DispatcherPortlet.doRenderService(DispatcherPortlet.java:789)
    at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:536)
Community
  • 1
  • 1
grek
  • 93
  • 1
  • 11
  • can you confirm that your `ajaxView.jsp` file is deployed/bundled with your application? It should be located inside your `/WEB-INF/jsp/` folder... – blurfus Mar 11 '15 at 19:08
  • I think this will guide you in the right direction: http://stackoverflow.com/questions/20214826/spring-2-5-ajax-1-7-updater-receive-wrong-response – blurfus Mar 11 '15 at 19:24
  • @ochi - appreciate your feedback. if you look at above, rendering delegates to 'ajaxView' which is AjaxView.java but not ajaxView.jsp. so AjaxView.java exists in the same package where the controller and using . makes sense? – grek Mar 11 '15 at 19:43
  • [@grek] sure, but your `viewResolver` is looking at JSPs - perhaps a new `ajaxViewResolver` is required then? see link above... – blurfus Mar 11 '15 at 19:45
  • I am a bit stumped and not enough time here to try it myself locally - let me see what else I can think of shortly – blurfus Mar 14 '15 at 00:24
  • using ajaxViewResolver produce the same result, always returns as text/html but not json. is there any other way to do asynchronous file upload in Spring-MVC-Portlet? your help will get me some sleep. Thanks! – grek Mar 14 '15 at 01:18
  • does this answer help? http://stackoverflow.com/a/5809142/600486 – blurfus Mar 14 '15 at 01:34
  • @ochi - Yes I'd tried this before. Works fine except Multipart file never submitted using ResourceRequest. if you refer the section 'What I've tried before' you will know. Thanks! – grek Mar 16 '15 at 17:53

1 Answers1

0

Here is the workaround:

Call1 - using Action request to get the file submitted to the controller, process the file and update the form bean.

Call2 - using Resource request to load the file data that was updated from Call1, return JSON data, extract it and update the view.

function uploadJqueryForm(){ 
    var fileForm = $("#fileForm");     
        return {
            uploadFile: function() {
                debugger;
                var that = this;
                var options = {
                    type: 'POST',
                    url: '<portlet:actionURL><portlet:param name="action" value="upload"/></portlet:actionURL>',
                    cache: false,
                    success: function(response){                            
                        that.uploadFileList();
                    }
                }                                   
                var ajaxForm = fileForm.ajaxForm(options);
                ajaxForm.submit();                  
            },
            uploadFileList: function() {                
                var that = this;
                    var options = {
                        type: 'POST',                       
                        url: '<portlet:resourceURL id="loadFile"/>',
                        cache: false,
                        success: function(response){                                
                            $.each(response, function (index, file) {                                   
                                var htmlrowStart = '<TR name="DataContainer">';
                                var htmlcol1 = '<TD name="DocName_ColumnData" valign="top" class="tableCell"> <SPAN name="DocName" class="outputData">' +file.attachmentName + '</SPAN> </TD>';                                 
                                $('#fileListTblID').append(htmlrowStart);
                                $('#fileListTblID').append(htmlcol1);
                                ...
                            });
                        }
                    }
                    var ajaxForm =  fileForm.ajaxForm(options);     
                    ajaxForm.submit();                  
            }           
        };
}
grek
  • 93
  • 1
  • 11