Although I'm answering late, I'll go ahead and include my answer. If you are using HTML 5,
try using HTML 5 - Server Sent Events (SSE) along with HTML 5 - Web Workers.
Note that SSE is not supported by MS IE as of now.
On Struts2 end, there could be a standard action to handle the request.
Also see this question.
You can also see the current compatibility here.
There is a demo here, where we can see the periodic requests being made (in network monitor) and gives a good idea of what can be done.
Also note that the URL (event source) can only be assigned once.
Update : From what I have looked around additionally (see here and here), SSE actually maintains a connection so as to receive periodic updates from the server. As such the server can also have an 'infinite loop' that goes on until the client terminates and then tries to reconnect in which case the server can again resend events.
Determining which events were missed by the client should be handled by the implementation if or as necessary.
Here is a link that demos SSE with a connection being maintained.
As per the spec we should also be able to send a 'Last-Event-ID' in the header. However yet to find an example that uses it!
Update 2 : An example that maintains connection and returns response using HttpServletResponse and another that repeatedly polls action and returns response using s2 stream result. Regarding frequency of polling when no connection is maintained, chrome appears to be 3 seconds where as firefox's is much larger.
1.) SSE.java
public class SSE extends ActionSupport {
public String handleSSE() {
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
System.out.println("Inside handleSSE()+suscribe "+Thread.currentThread().getName());
int timeout = 15*1000;
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
while((end - start) < timeout) {
try {
PrintWriter printWriter = response.getWriter();
printWriter.println( "data: "+new Date().toString() );
printWriter.println(); // note the additional line being written to the stream..
printWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
end = System.currentTimeMillis();
}
System.out.println("Exiting handleSSE()-suscribe"+Thread.currentThread().getName());
return SUCCESS;
}
}
2.) SSES2.java
public class SSES2 extends ActionSupport {
private InputStream sseStream;
public InputStream getSseStream() {
return sseStream;
}
public String handleSSES2() {
System.out.println("Inside handleSSES2() ");
String result = "data: "+new Date().toString() + "\n\n";
sseStream = new ByteArrayInputStream(result.getBytes() );
System.out.println("Exiting handleSSES2() ");
return SUCCESS;
}
}
3.) struts.xml
<struts>
<include file="strutsBase.xml"/>
</struts>
4.) strutsBase.xml
<struts>
<!-- Configuration for the default package. -->
<package name="strutsBase" extends="struts-default" >
<!-- Default interceptor stack. -->
<default-interceptor-ref name="basicStack"/>
<action name="suscribe" class="com.example.struts2.sse.action.SSE" method="handleSSE">
<result name="success">/view/empty.txt</result>
<!-- we don't need the full stack here -->
</action>
<action name="suscribeStateless" class="com.example.struts2.sse.action.SSES2" method="handleSSES2">
<result name="success" type="stream">
<param name="contentType">text/event-stream</param>
<param name="inputName">sseStream</param>
</result>
</action>
</package>
</struts>
sse.html
<!doctype html>
<meta charset="utf-8">
<title>EventSource demo</title>
<h1>new EventSource() for S2</h1>
<p><output id="result">OUTPUT VALUE</output></p>
<script>
(function(global, window, document) {
'use strict';
function main() {
window.addEventListener('DOMContentLoaded', contentLoaded);
}
function contentLoaded() {
var result = document.getElementById('result');
var stream = new EventSource('suscribe.action');
stream.addEventListener('message', function(event) {
var data = event.data;
result.value = data;
});
}
main();
})(this, window, window.document);
</script>
<!--
Also See :
http://server-sent-events-demo.herokuapp.com/
-->
sseAction.html
Same as sse.html except that EventSource url is 'suscribeStateless.action' instead of suscribe.action