掲示板

Handling asynchronous requests

thumbnail
13年前 に Jakub Liska によって更新されました。

Handling asynchronous requests

Regular Member 投稿: 187 参加年月日: 10/03/25 最新の投稿
Hey,

could please anybody tell me, what is the proper way of handling http requests, that are not supposed to be ActionRequest because of their asynchronous nature ? Especially Multipart http requests. According to JSR 286 it seems like Mutlipart http requests should be handled either in Action phase or as a servlet requests outside of portal.

For instance, Ajax Multipart requests from flash uploaders. They are not meant to be Action requests because Action phase is followed by render phase right ? As it would be convenient in case of html form.

Some frameworks like Spring are using ResourceRequests for handling common Ajax requests, but there isn't mentioned anything about it handling Multipart requests (in JSR 286).

Then what remains is Render phase, which could be used for that, but it doesn't seem to be right. In spring-portlet-mvc there is practically no way to do that in some cases, because render phase is expected to render a view, not handle requests asynchronously.

In spring framework there are then these issues that I don't know how to deal with

SPR-7662

SPR-7910

My final though is, that Asynchronous requests should be handled by a servlet and not pointed to portal servlet to become a portlet requests, which is quite annoying and it causes lot of extra work. Or, as it is in Spring-portlet-mvc, use ResourceRequests as a workaround.
thumbnail
13年前 に jelmer kuperus によって更新されました。

RE: Handling asynchronous requests

Liferay Legend 投稿: 1191 参加年月日: 10/03/10 最新の投稿
We specify windowState="exclusive" on the upload action url then set a render parameter that points to a json view, so the render result would actually return the success message to the upload component
thumbnail
13年前 に Jakub Liska によって更新されました。

RE: Handling asynchronous requests

Regular Member 投稿: 187 参加年月日: 10/03/25 最新の投稿
And you are talking about spring-portlet-mvc , or in general terms ?

I'm not sure I understand, you create an Action Request (as it is in document library uploader), set windowState="exclusive" so that portal doesn't create the html wrapper around the response.

And then, if you are using spring-portlet-mvc :

  • You can easily get Files from MultipartActionRequest
  • But ActionResponse doesn't have a writer as ResourceResonse does, so that you can't do something like this


	@ResourceMapping(value="sample")
    protected void getJsonData(ResourceRequest request, ResourceResponse response) throws JsonGenerationException, JsonMappingException, IOException {
 
	ObjectMapper mapper = new ObjectMapper();
        Map<string, string> numbers = new HashMap<string, string>();
        nameStruct.put("one", "1");
        nameStruct.put("two", "2");
        Map<string, object> data = new HashMap<string, object>();
        userData.put("numbers", number);
        userData.put("etc", "etc");
        
        response.setContentType(MediaType.APPLICATION_JSON.toString());
        mapper.writeValue(response.getWriter(), data);
        
     }</string,></string,></string,></string,>



  • hence you must have some viewResolver that would take care of various resolving strategies (jsp, json) and ideally MappingJacksonJsonView, but it currently exists for spring-web-mvc only


   
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order" value="1" />
        <property name="ignoreAcceptHeader" value="true" />
        <property name="mediaTypes">
            <map>
                <entry key="xml" value="application/xml" />
                <entry key="json" value="application/json" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <property name="marshaller" ref="jaxbMarshaller" />
                </bean>
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
                    <property name="objectMapper" ref="jaxbJacksonObjectMapper" />
                </bean>
            </list>
        </property>
</bean>



Without it it is really hard resolving for isntance JSPs and JSON views within a portlet / application context.
thumbnail
13年前 に jelmer kuperus によって更新されました。

RE: Handling asynchronous requests

Liferay Legend 投稿: 1191 参加年月日: 10/03/10 最新の投稿
But ActionResponse doesn't have a writer as ResourceResonse does, so that you can't do something like this


No I do something like this

    @ActionMapping
    public void upload(ActionResponse actionResponse, ...) {
       // get your MultipartFile and do funky stuff with it

       actionResponse.setRenderParameter("view", "success");
    }

    @RenderMapping(params = "view=success")
    public String success() throws Exception {
        return "someViewNameThatIsResolvedByABeanNameViewResolver";
    }



Remember the render phase follows the action phase. So by setting the render parameter view to success at the end of the action i know that the success render method will be called next. Because the exclusive windowmode is used , the portal does not decorate the response this method will generate. So you have full control over the output and you can write your json

In my case return a string that points to a view thats picked up by a component scan element that looks somewhat like this

@Component("someViewNameThatIsResolvedByABeanNameViewResolver")
public class BindingResultJsonView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<string, object> model, HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
       JSONObject json = JSONFactoryUtil.createJSONObject();
       // add stuff to the json
       PrintWriter writer = response.getWriter();
       writer.println(json.toString());
    }
}</string,>



My view application context looks somewhat like this


    

    <context:annotation-config />

    <context:component-scan base-package="...." />

    <bean id="portletMultipartResolver" class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver" />

    <bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <property name="order" value="1" />
    </bean>


    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="cache" value="false" />
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/path/" />
        <property name="suffix" value=".jsp" />
        <property name="order" value="2" />
    </bean>
thumbnail
13年前 に Jakub Liska によって更新されました。

RE: Handling asynchronous requests

Regular Member 投稿: 187 参加年月日: 10/03/25 最新の投稿
Thank you Jelmer,

I did exactly the same yesterday, but I ended up when ViewRendererServlet was trying to render my AjaxView. They were both in the same app context and I let spring instantiate it...so I don't know what the hell was that... But now I tried BeanNameViewResolver as you mentioned and it works like a charm...

Yesterday's code :


public class AjaxView extends AbstractView {
	private Logger logger = Logger.getLogger(AjaxView.class);

	public AjaxView() {
		super();
		setContentType("application/json");
	}

	@Override
	protected void renderMergedOutputModel(Map map, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		logger.info("Resolving ajax request view - " + map);
		JSONObject jsonObj = new JSONObject(map);
		logger.info("content Type = " + getContentType());
		// response.reset();
		response.setContentType("application/json");
		response.setCharacterEncoding("UTF-8");
		response.getWriter().write(jsonObj.toString());
		response.getWriter().flush();
}



	<import resource="common.xml" />
	
	<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping">
	    <property name="interceptors">
	         <bean class="org.springframework.web.portlet.handler.ParameterMappingInterceptor" />
	    </property>
 	</bean>
 	
 	<bean id="ajaxView" class="...spring.AjaxView" />
 	
 	<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
	
	<bean id="portletMultipartResolver" class="cz.instance.transl.util.LiferayMultipartNamespacesResolver" p:maxUploadSize="200480" p:portletNamespace="_2_WAR_brokerageportlet_" />

	<bean id="validator" class="....DocFormValidator" />
	<bean id="fileService" class="....FileService" />
	<bean id="validateService" class="....ValidateService" />


	<bean class="....UploadFormController" />
	<bean class="....PostUploadFormController" />
	<bean class="....AjaxUploadFormController">
		<property name="ajaxView" ref="ajaxView" />
	 </bean>



	@ActionMapping(params = "javax.portlet.action=test")
	public void response(MultipartActionRequest request, ActionResponse response) {

		response.setRenderParameter("javax.portlet.action", "test2");

		List<multipartfile> fileList = request.getFiles("file");
	}

	@RequestMapping(params = "javax.portlet.action=example22")
	public ModelAndView process(RenderRequest request, Model model) throws IOException {
		Map map = new HashMap();
		map.put("test", new Integer(2));
		return new ModelAndView(ajaxView, map);
	}
</multipartfile>


Strange errors



Caused by: java.lang.IllegalStateException: WebApplicationObjectSupport instance [cz.instance.transl.spring.AjaxView: name 'ajaxView'] does not run within a ServletContext. Make sure the object is fully configured!
        at org.springframework.web.context.support.WebApplicationObjectSupport.getServletContext(WebApplicationObjectSupport.java:126)
        at org.springframework.web.servlet.view.AbstractView.createRequestContext(AbstractView.java:267)
12年前 に SZ khan によって更新されました。

RE: Handling asynchronous requests

Regular Member 投稿: 145 参加年月日: 09/10/31 最新の投稿
Hi Jakub,
Did you solve this problem of yours

does not run within a ServletContext. Make sure the object is fully configured!


as I am getting the same problem using

org.springframework.web.servlet.view.ResourceBundleViewResolver

Thanks,
Sultee
thumbnail
12年前 に Jakub Liska によって更新されました。

RE: Handling asynchronous requests

Regular Member 投稿: 187 参加年月日: 10/03/25 最新の投稿
I just used BeanNameViewResolver as Jelmer suggested and it worked.
11年前 に sona sun によって更新されました。

RE: Handling asynchronous requests

New Member 投稿: 1 参加年月日: 12/07/26 最新の投稿
Hi I am facing same issue,

How does your jsp looks like , I am trying to submit a form which I do not want to.
So the approach which you are following does that submits form
9年前 に TyFiJC TY によって更新されました。

RE: Handling asynchronous requests

New Member 投稿: 1 参加年月日: 15/03/11 最新の投稿
Hello there - would you help me with the below:

I ran into the same issue and followed the way suggested above, so after the rendering phase, it suppose to component scan and find the view for e.g. AjaxView.java, but instead it looking for the view /WEB-INF/jsp/ajaxView.jsp to delegate to. below is the exception stack trace, any idea what is going on here? using Spring MVC Portlet v4.1.5 in WebSphere Portal v8


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)