[richfaces-issues] [JBoss JIRA] (RF-13674) a4j:push broken on WebLogic 12c in Chrome

Val Blant (JIRA) issues at jboss.org
Wed Jun 18 19:57:24 EDT 2014


     [ https://issues.jboss.org/browse/RF-13674?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Val Blant updated RF-13674:
---------------------------

    Description: 
<a4j:push />  works properly on Tomcat 7, but fails on WebLogic 12c in Chrome (Firefox works fine).

{code:title=Page.xhtml|borderStyle=solid}
<a4j:push address="systemLinks" >  
  <a4j:ajax event="dataavailable" render="systemLinksPanel" />  
</a4j:push>  
  
<a4j:outputPanel id="systemLinksPanel">  
     stuff here  
</a4j:outputPanel>  
{code}

{code:title=Java Code}
TopicKey topicKey = new TopicKey("systemLinks");  
TopicsContext topicsContext = TopicsContext.lookup();  
topicsContext.publish(topicKey, "fly, you fools!");  
{code}

On Tomcat 7 this work perfectly, and always refreshes '_systemLinksPanel_' when data is published. 

On Weblogic 12c the data is not flushed correctly. If you use Wireshark, you'd see the following response coming back from the server:
{panel:title=Tomcat 7}
3b
<"topic":"systemLinks","data":"fly, you fools!","number":0>
0
{panel}

{panel:title=Weblogic 12c}
003b
<"topic":"systemLinks","data":"fly, you fools!","number":0>
{panel}

*Note the missing zero terminator line in the WebLogic response!*

I narrowed down the problem to the following:
{code:title=org.richfaces.application.push.impl.RequestImpl}
    public synchronized void onBroadcast(AtmosphereResourceEvent<HttpServletRequest, HttpServletResponse> event) {
        MessageDataScriptString serializedMessages = (MessageDataScriptString) event.getMessage();
        getSession().clearBroadcastedMessages(serializedMessages.getLastSequenceNumber());

        hasActiveBroadcaster = false;

        if (isPolling()) {
            event.getResource().resume(); // <= THIS LINE
        } else {
            postMessages();
        }
    }
{code}

There is nothing wrong with this in principle, but it does not work due to a bug in Atmosphere 0.8.4. Calling _AtmosphereResource.resume()_ at this point seems reasonable, since it is supposed to properly finish/commit the _HttpServletResponse_.  However, this does not work on WebLogic 12c and leads to incorrectly flushed data demonstrated above. 

The underlying problem seems to be the fact that _AtmosphereResource.resume()_ method interrupts the thread which flushed the socket Writer before a call to _asyncContext.complete()_ is made:
{code}
public AtmosphereResource resume() {
....
 if (!b.isDestroyed()) {
     b.removeAtmosphereResource(event.getResource());
 }
....
 asyncSupport.action(this); // <= This is where asyncContext.complete() is called
}
{code}

I have verified that calling _asyncContext.complete()_ before we clean up Atmosphere resources fixes the problem.

Here's the fix, which needs to go into _RequestImpl.onBroadcast_:
{code:title=org.richfaces.application.push.impl.RequestImpl}
    public synchronized void onBroadcast(AtmosphereResourceEvent<HttpServletRequest, HttpServletResponse> event) {
  ..............
        if (isPolling()) {

// This is the workaround. Completing AsyncContext before cleaning up Atmosphere resources
// seems to make this work everywhere.
//
AsyncContext asyncContext = (AsyncContext) 
	event
		.getResource()
		.getRequest()
		.getAttribute("org.atmosphere.container.asyncContext");

   if ( asyncContext != null ) {
	asyncContext.complete();
   }

            event.getResource().resume();
        } else {
            postMessages();
        }
    }
  ..............
{code}

I am attaching a very simple WAR file that demonstrates the problem clearly. The demo app does not use Richfaces, b/c I wanted to remove all unnecessary complexity. Instead I created a couple of simple classes that mimic the behavior of _org.richfaces.webapp.PushHandlerFilter_ and _org.richfaces.application.push.impl.RequestImpl_. In the demo app, _MeteorRequest = RequestImpl_, which is where the fix needs to go.

Please note that this issue is fixed in Atmosphere 2.1. I ran the demo jar with v2.1.5, and it works correctly, so the easiest fix on the RichFaces side could be to simply upgrade the underlying Atmosphere framework. The API differences, at least as far as the PushHandlerFilter is concerned are minimal.

Now, the reason why only Chrome is effected has to do with processing of chunked responses by the XMLHttpRequest. Unlike other browsers, Chrome is not capable of handling improperly terminated chunked responses. So:

{noformat}
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate
Date: Wed, 18 Jun 2014 05:37:55 GMT
Pragma: no-cache
Transfer-Encoding: chunked <<--- THIS IS IMPORTANT
Content-Type: text/plain
Expires: -1
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
X-Powered-By: Servlet/3.0 JSP/2.2

0841
<!-- ---------------------------------------------------------------- http://github.com/Atmosphere ------------------------------------------------------------------------ -->
<!-- Welcome to the Atmosphere Framework. To work with all the browsers when suspending connection, Atmosphere must output some data to makes WebKit based browser working.-->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------001c
Wed Jun 18 13:37:57 CST 201
<<--- MISSING 0 TERMINATOR
{noformat}

This can be fixed by explicitly setting Content-Length in the response, as per these discussions:
http://stackoverflow.com/questions/22219565/iis-chrome-failed-to-load-resource-neterr-incomplete-chunked-encoding
http://www.rahulsingla.com/blog/2010/06/asp-net-sets-the-transfer-encoding-as-chunked-on-premature-flushing-the-response

However, that does not seem to be feasible here, since we must reply with a header of known length, followed by the data of unknown length.

  was:
<a4j:push />  works properly on Tomcat 7, but fails on WebLogic 12c in Chrome (Firefox works fine).

{code:title=Page.xhtml|borderStyle=solid}
<a4j:push address="systemLinks" >  
  <a4j:ajax event="dataavailable" render="systemLinksPanel" />  
</a4j:push>  
  
<a4j:outputPanel id="systemLinksPanel">  
     stuff here  
</a4j:outputPanel>  
{code}

{code:title=Java Code}
TopicKey topicKey = new TopicKey("systemLinks");  
TopicsContext topicsContext = TopicsContext.lookup();  
topicsContext.publish(topicKey, "fly, you fools!");  
{code}

On Tomcat 7 this work perfectly, and always refreshes '_systemLinksPanel_' when data is published. 

On Weblogic 12c the data is not flushed correctly. If you use Wireshark, you'd see the following response coming back from the server:
{panel:title=Tomcat 7}
3b
<"topic":"systemLinks","data":"fly, you fools!","number":0>
0
{panel}

{panel:title=Weblogic 12c}
003b
<"topic":"systemLinks","data":"fly, you fools!","number":0>
{panel}

*Note the missing zero terminator line in the WebLogic response!*

I narrowed down the problem to the following:
{code:title=org.richfaces.application.push.impl.RequestImpl}
    public synchronized void onBroadcast(AtmosphereResourceEvent<HttpServletRequest, HttpServletResponse> event) {
        MessageDataScriptString serializedMessages = (MessageDataScriptString) event.getMessage();
        getSession().clearBroadcastedMessages(serializedMessages.getLastSequenceNumber());

        hasActiveBroadcaster = false;

        if (isPolling()) {
            event.getResource().resume(); // <= THIS LINE
        } else {
            postMessages();
        }
    }
{code}

There is nothing wrong with this in principle, but it does not work due to a bug in Atmosphere 0.8.4. Calling _AtmosphereResource.resume()_ at this point seems reasonable, since it is supposed to properly finish/commit the _HttpServletResponse_.  However, this does not work on WebLogic 12c and leads to incorrectly flushed data demonstrated above. 

The underlying problem seems to be the fact that _AtmosphereResource.resume()_ method interrupts the thread which flushed the socket Writer before a call to _asyncContext.complete()_ is made:
{code}
public AtmosphereResource resume() {
....
 if (!b.isDestroyed()) {
     b.removeAtmosphereResource(event.getResource());
 }
....
 asyncSupport.action(this); // <= This is where asyncContext.complete() is called
}
{code}

I have verified that calling _asyncContext.complete()_ before we clean up Atmosphere resources fixes the problem.

Here's the fix, which needs to go into _RequestImpl.onBroadcast_:
{code:title=org.richfaces.application.push.impl.RequestImpl}
    public synchronized void onBroadcast(AtmosphereResourceEvent<HttpServletRequest, HttpServletResponse> event) {
  ..............
        if (isPolling()) {

// This is the workaround. Completing AsyncContext before cleaning up Atmosphere resources
// seems to make this work everywhere.
//
AsyncContext asyncContext = (AsyncContext) 
	event
		.getResource()
		.getRequest()
		.getAttribute("org.atmosphere.container.asyncContext");

   if ( asyncContext != null ) {
	asyncContext.complete();
   }

            event.getResource().resume();
        } else {
            postMessages();
        }
    }
  ..............
{code}

I am attaching a very simple WAR file that demonstrates the problem clearly. The demo app does not use Richfaces, b/c I wanted to remove all unnecessary complexity. Instead I created a couple of simple classes that mimic the behavior of _org.richfaces.webapp.PushHandlerFilter_ and _org.richfaces.application.push.impl.RequestImpl_. In the demo app, _MeteorRequest = RequestImpl_, which is where the fix needs to go.

Please note that according to the Atmosphere author, this problem is fixed in 2.1 branch. Here's the discussion: https://groups.google.com/forum/#!topic/atmosphere-framework/KCKlSmbMrRo

Now, the reason why only Chrome is effected has to do with processing of chunked responses by the XMLHttpRequest. Unlike other browsers, Chrome is not capable of handling improperly terminated chunked responses. So:

{noformat}
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate
Date: Wed, 18 Jun 2014 05:37:55 GMT
Pragma: no-cache
Transfer-Encoding: chunked <<--- THIS IS IMPORTANT
Content-Type: text/plain
Expires: -1
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
X-Powered-By: Servlet/3.0 JSP/2.2

0841
<!-- ---------------------------------------------------------------- http://github.com/Atmosphere ------------------------------------------------------------------------ -->
<!-- Welcome to the Atmosphere Framework. To work with all the browsers when suspending connection, Atmosphere must output some data to makes WebKit based browser working.-->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
<!-- --------------------001c
Wed Jun 18 13:37:57 CST 201
<<--- MISSING 0 TERMINATOR
{noformat}

This can be fixed by explicitly setting Content-Length in the response, as per these discussions:
http://stackoverflow.com/questions/22219565/iis-chrome-failed-to-load-resource-neterr-incomplete-chunked-encoding
http://www.rahulsingla.com/blog/2010/06/asp-net-sets-the-transfer-encoding-as-chunked-on-premature-flushing-the-response

However, that does not seem to be feasible here, since we must reply with a header of known length, followed by the data of unknown length.



> a4j:push broken on WebLogic 12c in Chrome
> -----------------------------------------
>
>                 Key: RF-13674
>                 URL: https://issues.jboss.org/browse/RF-13674
>             Project: RichFaces
>          Issue Type: Bug
>      Security Level: Public(Everyone can see) 
>          Components: component-push/poll
>    Affects Versions: 4.2.0.Final
>         Environment: WebLogic 12c
>            Reporter: Val Blant
>         Attachments: atmosphere-weblogic12c-bug-0.8.4-sources.jar, atmosphere-weblogic12c-bug-0.8.4.war, RichFacesPushFixFilter.java
>
>
> <a4j:push />  works properly on Tomcat 7, but fails on WebLogic 12c in Chrome (Firefox works fine).
> {code:title=Page.xhtml|borderStyle=solid}
> <a4j:push address="systemLinks" >  
>   <a4j:ajax event="dataavailable" render="systemLinksPanel" />  
> </a4j:push>  
>   
> <a4j:outputPanel id="systemLinksPanel">  
>      stuff here  
> </a4j:outputPanel>  
> {code}
> {code:title=Java Code}
> TopicKey topicKey = new TopicKey("systemLinks");  
> TopicsContext topicsContext = TopicsContext.lookup();  
> topicsContext.publish(topicKey, "fly, you fools!");  
> {code}
> On Tomcat 7 this work perfectly, and always refreshes '_systemLinksPanel_' when data is published. 
> On Weblogic 12c the data is not flushed correctly. If you use Wireshark, you'd see the following response coming back from the server:
> {panel:title=Tomcat 7}
> 3b
> <"topic":"systemLinks","data":"fly, you fools!","number":0>
> 0
> {panel}
> {panel:title=Weblogic 12c}
> 003b
> <"topic":"systemLinks","data":"fly, you fools!","number":0>
> {panel}
> *Note the missing zero terminator line in the WebLogic response!*
> I narrowed down the problem to the following:
> {code:title=org.richfaces.application.push.impl.RequestImpl}
>     public synchronized void onBroadcast(AtmosphereResourceEvent<HttpServletRequest, HttpServletResponse> event) {
>         MessageDataScriptString serializedMessages = (MessageDataScriptString) event.getMessage();
>         getSession().clearBroadcastedMessages(serializedMessages.getLastSequenceNumber());
>         hasActiveBroadcaster = false;
>         if (isPolling()) {
>             event.getResource().resume(); // <= THIS LINE
>         } else {
>             postMessages();
>         }
>     }
> {code}
> There is nothing wrong with this in principle, but it does not work due to a bug in Atmosphere 0.8.4. Calling _AtmosphereResource.resume()_ at this point seems reasonable, since it is supposed to properly finish/commit the _HttpServletResponse_.  However, this does not work on WebLogic 12c and leads to incorrectly flushed data demonstrated above. 
> The underlying problem seems to be the fact that _AtmosphereResource.resume()_ method interrupts the thread which flushed the socket Writer before a call to _asyncContext.complete()_ is made:
> {code}
> public AtmosphereResource resume() {
> ....
>  if (!b.isDestroyed()) {
>      b.removeAtmosphereResource(event.getResource());
>  }
> ....
>  asyncSupport.action(this); // <= This is where asyncContext.complete() is called
> }
> {code}
> I have verified that calling _asyncContext.complete()_ before we clean up Atmosphere resources fixes the problem.
> Here's the fix, which needs to go into _RequestImpl.onBroadcast_:
> {code:title=org.richfaces.application.push.impl.RequestImpl}
>     public synchronized void onBroadcast(AtmosphereResourceEvent<HttpServletRequest, HttpServletResponse> event) {
>   ..............
>         if (isPolling()) {
> // This is the workaround. Completing AsyncContext before cleaning up Atmosphere resources
> // seems to make this work everywhere.
> //
> AsyncContext asyncContext = (AsyncContext) 
> 	event
> 		.getResource()
> 		.getRequest()
> 		.getAttribute("org.atmosphere.container.asyncContext");
>    if ( asyncContext != null ) {
> 	asyncContext.complete();
>    }
>             event.getResource().resume();
>         } else {
>             postMessages();
>         }
>     }
>   ..............
> {code}
> I am attaching a very simple WAR file that demonstrates the problem clearly. The demo app does not use Richfaces, b/c I wanted to remove all unnecessary complexity. Instead I created a couple of simple classes that mimic the behavior of _org.richfaces.webapp.PushHandlerFilter_ and _org.richfaces.application.push.impl.RequestImpl_. In the demo app, _MeteorRequest = RequestImpl_, which is where the fix needs to go.
> Please note that this issue is fixed in Atmosphere 2.1. I ran the demo jar with v2.1.5, and it works correctly, so the easiest fix on the RichFaces side could be to simply upgrade the underlying Atmosphere framework. The API differences, at least as far as the PushHandlerFilter is concerned are minimal.
> Now, the reason why only Chrome is effected has to do with processing of chunked responses by the XMLHttpRequest. Unlike other browsers, Chrome is not capable of handling improperly terminated chunked responses. So:
> {noformat}
> HTTP/1.1 200 OK
> Cache-Control: no-store, no-cache, must-revalidate
> Date: Wed, 18 Jun 2014 05:37:55 GMT
> Pragma: no-cache
> Transfer-Encoding: chunked <<--- THIS IS IMPORTANT
> Content-Type: text/plain
> Expires: -1
> Access-Control-Allow-Credentials: true
> Access-Control-Allow-Origin: *
> X-Powered-By: Servlet/3.0 JSP/2.2
> 0841
> <!-- ---------------------------------------------------------------- http://github.com/Atmosphere ------------------------------------------------------------------------ -->
> <!-- Welcome to the Atmosphere Framework. To work with all the browsers when suspending connection, Atmosphere must output some data to makes WebKit based browser working.-->
> <!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
> <!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
> <!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
> <!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
> <!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
> <!-- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- -->
> <!-- --------------------001c
> Wed Jun 18 13:37:57 CST 201
> <<--- MISSING 0 TERMINATOR
> {noformat}
> This can be fixed by explicitly setting Content-Length in the response, as per these discussions:
> http://stackoverflow.com/questions/22219565/iis-chrome-failed-to-load-resource-neterr-incomplete-chunked-encoding
> http://www.rahulsingla.com/blog/2010/06/asp-net-sets-the-transfer-encoding-as-chunked-on-premature-flushing-the-response
> However, that does not seem to be feasible here, since we must reply with a header of known length, followed by the data of unknown length.



--
This message was sent by Atlassian JIRA
(v6.2.6#6264)


More information about the richfaces-issues mailing list