I am using Tomcat 7. When my asynchronous servlet tries to PUSH partial response to client at different intervals, it does not work.. The response gets flushed only after the whole response is ready.
How to PUSH partial response?
Here is my code
#WebServlet(urlPatterns={"/home"} , name="asynch", asyncSupported=true)
public class CometServlet extends HttpServlet {
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
{
final AsyncContext ac = request.startAsync();
new MyThread(ac).start();
}
}
What MyThread does is it will write numbers from 1 to 10 to the reponse object at regular intervals.
class MyThread extends Thread
{
AsyncContext ac;
public MyThread( AsyncContext ac ) {
this.ac = ac;
}
public void run()
{
int i =2 ;
while( i < 10 )
{
try
{
ac.getResponse().getWriter().print(i + "\n" );
ac.getResponse().getWriter().flush();
ac.getResponse().flushBuffer();
Thread.sleep(1000);
}
catch (Exception e) {
System.out.println("ccttt " + e);
}
i++;
}
ac.complete();
}
}
And the page that requests the servlet
<script type="text/javascript">
function show()
{
var xml = new XMLHttpRequest();
xml.open("GET", "http://localhost:8080/Comet/home", true );
xml.onreadystatechange=function()
{
if (xml.readyState== 3|| xml.readyState == 4 ) {
document.getElementById("dynamicContent").innerHTML=xml.responseText;
}
}
xml.send(null);
}
</script>
<input type="button" value="show" onclick="show()"/>
<div id="dynamicContent"> </div>
After about 10 seconds I see the complete response on the browser. But I want to see the partial outputs every second.
All i want to learn is how to PUSH partial response with Servlet 3 API.
Can someone answer this question?
Please see my answer for this exact same problem mentioned in another question.
If you still face this problem after following the instructions in that answer, do let me know.
Related
This makes me really curious.There is a button which sends a simple post request by ajax on a jsp page,and I use a RESTFUL method to handle this request,but that method will be executed twice or three times.This will only happen on CentOS 7.3,on my laptop I use windows10, multi-thread will not happen.I have searched on Google but nothing helpful
has been found.Here are the codes:
asset.jsp:
<button class="btn btn-default btn-sm" title="allDoMi">
<i class="fa fa-arrow-down">allDoMi</i>
</button>
$("button[title='allDoMi']").click(function () {
var dataparam = "version=1";
if (!confirm('confirm all DoMi?'))
return;
//ajax request
$.ajax({
contentType: "application/json",
url: serviceurl.baseurl + "/asset/doMiAction",
data: dataparam,
beforeSend: function () {
//do nothing
},
error: function () {
//do nothing
},
success: function (info) {
//do nothing
}
});
});
Asset.java
#Service
#Path("/asset")
public class AssetRest {
#Path("/doMiAction")
#POST
#Produces({ MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML })
public RestfulResult doMiAction(#FormParam("version") String
version) {
logger.info("doMiAction method began....");
//package data for duMiSyncDtos,here only for test
List<DuMiSyncDto> duMiSyncDtos =new List<>();
//this url is for http simulation using HttpURLConnection
final String dumiUrl = "http://someip/someurl";
final Map<String, List<DuMiSyncDto>> map;
//only continue when list is not empty
if (!duMiSyncDtos.isEmpty()) {
//this method used for sync data in a certain order
map = groupList(duMiSyncDtos);
SortedSet<String> ss = new TreeSet<>(map.keySet());
final Iterator<String> iter = ss.iterator();
final ScheduledExecutorService ticker = Executors.newSingleThreadScheduledExecutor();
logger.info("NEW A SINGLETHREADSCHEDULEDEXECUTOR");
//retrieve data from a .property file,I set it 20000,therefore the job will be executed in every 20 seconds
final int DELAY = NumberUtils.toInt(WebUtils.getConfigValue("dumi.delay"));
ticker.scheduleWithFixedDelay(new Runnable() {
private int count;
public void run() {
logger.info("BEGIN RUN METHOD:"+System.identityHashCode(AssetRest.this));
if(iter.hasNext()) {
try {
List<DuMiSyncDto> value = map.get(iter.next());
//this method used for simulating a httprequest using HttpURLConnection to invoke a remote service to get the result info which forms in a JSON string format
String resultmsg = getDuMiReturnMessage(dumiUrl,value);
if(resultmsg!=null && !resultmsg.contains("\"code\":\"0000\"")) {
logger.info("Return code is "+resultmsg+",the sync work will be terminated.");
ticker.shutdown();
return;
}
//this method used for showing some useful infomation on the console using log4j
showSyncInfomation(value);
//this method used for count how many items have been synchronized successfully
int currentcount = getSyncCount(resultmsg);
count += currentcount ;
logger.info("current sync data:"+currentcount+",summing data"+count+"");
} catch (Exception e) {
logger.error("method[doMiAction]...executing schedule:",e);
}
} else {
ticker.shutdown();
}
}
}, 0, DELAY, TimeUnit.MILLISECONDS);
}
}
After I click the button,all the log info will be shown on Putty console for two or three times,yet I have clicked that button for only ONCE!I have tested for several times,it will happen,but in windows on my laptop,this will not happen at all.Here is a detail might be help:previously the implementation for timed execution is not like this,it has been written like :
for(DuMiSyncDto dto:duMiSyncDtoList){
//do the business code
Thread.sleep(20000);
}
Because there is database synchronization from the remote service,I need to control the interval time not too soon between every two operations:execute in every 20 seconds and 100 data at a time.In this situation,the multi-thread problem occurs,I thought it may be the for loop which aroused so I change the way using a JDK API instead but issues were still there.So WHY all of these?
---------------------------first edit------------------------------------------
private int getSyncCount(String resultmsg) {
int count = 0;
JSONObject obj = JSONObject.fromObject(resultmsg);
String message = obj.getString("message");
if(!WebUtils.isEmpty(message)) {
String[] arr = message.split(" ");
if(arr!=null && arr.length>1) {
count += Integer.parseInt(arr[1].trim());
}
}
logger.info("currentThreadName:"+Thread.currentThread().getName());
return count;
}
Notice in this method,I log the current thread name,and it shows :
...
currentThreadName:pool-1-thread-1
currentThreadName:pool-2-thread-1
currentThreadName:pool-3-thread-1
...
when there are 3 threads.
I want to modify the HTML body tag when I open a Wicket-Bootstrap Modal. What I'm trying to achieve is <body class="modal-open"> instead of <body>
Using Wicket 8 M8 , I have this code:
owsImportDialog = new MyModalBootstrapDialog("owsImportDialog"
, new CompoundPropertyModel<>(new BopOwsTO())) {
#Override
void importOws(AjaxRequestTarget target, IModel<BopOwsTO> owsModel) {
appendCloseDialogJavaScript(target);
BopOwsTO owsTo = owsModel.getObject();
try {
importOwsCapabilities(owsTo);
owsViewDialog.header(Model.of("OWS anzeigen"))
.setModel(Model.of(owsTo.getServiceId()));
owsViewDialog.appendShowDialogJavaScript(target);
}
catch (OwsCapsImportException e) {
String localizedMessage = e.getLocalizedMessage();
importAlert.setModelObject(localizedMessage);
importAlert.appendShowDialogJavaScript(target);
error(localizedMessage);
}
finally {
target.appendJavaScript("document.getElementsByTagName('body')[0]" +
".setAttribute('class', 'modal-open');");
// target.appendJavaScript("document.body.setAttribute('class', 'modal-open');");
// target.prependJavaScript("document.body.setAttribute('class', 'modal-open');");
// target.appendJavaScript("alert('Hallo');");
// owsViewDialog is a child of owsView WebMarkupContainer
target.add(owsView, feedback);
}
}
#Override
void saveOws(AjaxRequestTarget target, IModel<BopOwsTO> owsModel)
{ }
#Override
void cancel(AjaxRequestTarget target)
{ }
};
If the line target.appendJavaScript("alert('Hallo');"); is active I actually see the alert window.
I also tried this code in the page class:
#Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
PackageResourceReference resourceReference = new PackageResourceReference(
getClass(), "../css/BuiOwsPage.css");
CssReferenceHeaderItem cssRef = CssReferenceHeaderItem.forReference(resourceReference);
response.render(cssRef);
response.render(OnLoadHeaderItem
.forScript("document.body.setAttribute('class', 'modal-open');"));
}
But none of my attempts was succesful.
Update
The answer of #martin-g didn't solve the issue.
I'm quite sure that the problem is caused by the sequence of these statements:
{
appendCloseDialogJavaScript(target);
...
try {
owsViewDialog.appendShowDialogJavaScript(target);
....
}
catch { ... }
finally {
target.add(owsView, feedback);
}
}
When this modal is closed because of appendCloseDialogJavaScript() ,
the class modal-open is erased from the class attribute of the <body> .
Then owsViewDialog opens, but modal-open isn't inserted in class, no matter if I append the snippet jQuery(document.body).addClass('modal-open') or not. The missing modal-open means that the page can't be scrolled.
Since Wicket and Bootstrap are used then jQuery is also available. I would recommend you to use jQuery(document.body).addClass('modal-open').
There must be a reason why jQuery has both addClass() and attr()!
Hello I am using a webservice which returns a output upon completion of code execution. Is it possible that webservice may return the status in chunks like custom strings: Test Started, Test In Progress, Test Completed etc.
What I need to do to achieve this. Here is my current code where I am expecting a json string as input, supplied json is parsed and further processing is being performed.
//Class
public class WebserviceClient
{
/** calling constructor to initialize logger */
Utils c = new Utils();
Logger logger = Logger.getLogger(WebserviceClient.class.getName());
#Path("/test")
#POST
#Consumes(MediaType.APPLICATION_JSON)
//#Produces(MediaType.APPLICATION_JSON)
public String processRequest(final String inputData)
{
String executionID = "NOT_FOUND" ;
String result = "";
try
{
/** creating a pool of threads to submit a task to a callable thread */
ExecutorService ex = Executors.newFixedThreadPool(5);
Future<String> futureObject = ex.submit(new Callable<String>() {
#Override
public String call() throws Exception
{
logger.info("Parsing Received Request: "+inputData);
String rID = new JSONObject(inputData).getString("id");
logger.info("Received Id: "+rID + " From Request: "+inputData);
if(new RunTest().isTestCompleted(rID))
{
return rID;
}
else
{
return "777";
}
}
});
result = futureObject.get();
if(futureObject.get()!=null)
{
ex.shutdown();
}
else{
logger.debug("call id: "+executionID +" result is not generated yet. ");
}
logger.info("call id && Result: "+result);
}
catch(Exception e)
{
logger.error("call id: "+executionID, e);
}
return result;
}
}
You need to do a continuous polling to the server at high frequency to achieve the current status.
For little more information have a look at the :
Continuous polling of output using spring ,rest and angular js
This includes design consideration of using WebSockets etc, but there is no straight forward solution that I'm aware of.
I am trying to make a servlet to download files online. For this, I have made a progress bar element in jsp.
<progress id="p1" max="100" value="0"><span>0</span>%</progress>
and the java script code to update the progress value:
function setProgress(value)
{
var progressBar = document.getElementById("p1");
progressBar.value = value;
progressBar.getElementsByTagName('span')[0].textContent = value;
}
Now, In the servlet code to change the progress:
InputStream is = ..........;
byte[] bytes = new byte[size*1024];
int read = in.read();
for(int j=0;read!=-1;j++)
{
bytes[j] = (byte)read;
setProgress((int)getProgressDownload(bytes.length));
read = in.read();
}
public float getProgressDownload(int dsize)
{
return ((float)dsize/tsize)*100;//tsize is total file's size;
}
public void setProgress(int value)
{
try
{
response.getWriter().write("<script>");
response.getWriter().write("setProgress(\"p1\","+value+");");
response.getWriter().write("</script>");
}
catch(Exception e)
{
e.printStackTrace();
}
}
Now the problem is that is makes the HTML code lengthy because for every byte it will print a script code.
What should I do to prevent this?
Thanks for help
Hi first I think you should store the process in session. And then using ajax call you can regularly update the progress bar using response received from below some servlet. Add the mapping and all I am just writing the doGet stuff..
Also I assume URL mapping as 'progressServlet' for this particular servlet..
Below is doGet method
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String downloadId = "longProcess_" + request.getParameter("downloadId");
LongProcess longProcess = (LongProcess) request.getSession().getAttribute(downloadId);
int progress = longProcess.getProgress();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(String.valueOf(progress));
}
And then in JS you can use setTimeout() /setInterval() to call checkProgress method given below at certain intervals and stop calling if progress received is 100% complete. You can use this to repeatedly fire Ajax requests to request current status of the progress.
var refreshprogessbar = setInterval(checkProgress, 10000);// 10 seconds
function checkProgress() {/*pass the actual id instead of 12345*/
$.getJSON('progressServlet?downloadId=12345', function(progress) {
if(progress == 100){clearInterval(refreshprogessbar);}
setProgress(progress);
});
}
LongProcess is basically the class you use to keep track of the on going process. Below is the example of one of such:
class LongProcess extends Thread {
private int progress;
public void run() {
while (progress < 100) {
try { sleep(1000); } catch (InterruptedException ignore) {}
progress++;
}
}
public int getProgress() {
return progress;
}
}
I am just incrementing the progress filed member but you can have a setProgress() method and add logic to increase the progress.
i know the question may sound easy to most of you but I am stuck with it.
First of all i like to define what i am trying to achieve.
on eclipse i am running a piece of code that sends some data over specific port, and via html and javascript i am getting those that it's sent and print them on screen.
I have an account from one of free hosting websites.
I want to run my code on that website e.g mywebsite.blahblah.com/...
and from html file on my computer i want to access that website, get those values produced by java code and print them on screen.
I have no idea where to start.
the codes are
java and html
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import org.java_websocket.WebSocket;
import org.java_websocket.WebSocketImpl;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
public class GPSServer extends WebSocketServer {
static int port = 9876;
public GPSServer(int port) throws UnknownHostException {
super(new InetSocketAddress(port));
}
public GPSServer(InetSocketAddress address) {
super(address);
}
public void sendData(String s) {
Collection<WebSocket> con = connections();
synchronized (con) {
for (WebSocket c : con) {
c.send(s);
}
}
}
#Override
public void onOpen(WebSocket arg0, ClientHandshake arg1) {
System.out.println(arg0.getRemoteSocketAddress().getAddress()
.getHostAddress()
+ " connected to the server!");
}
#Override
public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) {
System.out.println(arg0 + " disconnected!");
}
#Override
public void onError(WebSocket arg0, Exception arg1) {
arg1.printStackTrace();
if (arg0 != null) {
}
}
#Override
public void onMessage(WebSocket arg0, String arg1) {
System.out.println(arg0 + ": " + arg1);
}
public static Runnable sendData() {
Runnable r = new Runnable() {
#Override
public void run() {
WebSocketImpl.DEBUG = true;
GPSServer server;
try {
server = new GPSServer(GPSServer.port);
server.start();
System.out.println("GPS server started at port: "
+ server.getPort());
double longitude = 39.55;
double latitude = 22.16;
String lng = Double.toString(longitude);
String ltd = Double.toString(latitude);
String all = lng + "-" + ltd;
while (true) {
server.sendData(all);
/*
* server.sendData(Double.toString(longitude));
* System.out.println("longitude sent...");
* server.sendData(Double.toString(latitude));
* System.out.println("latitude sent...");
*/
Thread.sleep(5000);
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
};
return r;
}
public static void main(String[] args) throws UnknownHostException {
Thread thread = new Thread(GPSServer.sendData());
thread.start();
}
}
--
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function WebSocketTest()
{
var lat;
var lng;
if ("WebSocket" in window)
{
alert("WebSocket is supported by your Browser!");
console.log("WebSocket is supported by your Browser!");
// Let us open a web socket
var ws = new WebSocket("ws://localhost:9876/echo");
ws.onopen = function()
{
ws.send("Message to send");
alert("Message is sent...");
};
ws.onmessage = function (evt) {
var partsArray = evt.data.split('-');
lng=partsArray[0];
lat=partsArray[1];
alert(lat);
alert(lng);
};
ws.onclose = function() {
alert("Connection is closed...");
console.log("Connection is closed...");
};
}
else
{
alert("WebSocket NOT supported by your Browser!");
}
}
</script>
</head>
<body>
<div id="sse">
Run WebSocket
</div>
<div>
<p id="para"> BASIC HTML!</p>
</div>
</body>
</html>
Thanks!
I'm assuming you're very new to all this web development. I haven't studied your code fully but the basic idea is you need a server side scripting language like JSP(of course JSP because you're using Java Code). I hope you know Javascript's basic idea is to use resources on the client's end, or to load data dynamically. So if you're only concerned with displaying some values from server to the client, you can simple make a servlet which will print your data.
Following MVC pattern,
Controller== Make a servlet which will handle the request made by user(i.e. the link which will show data,basically). Set your Model in this controller once you receive a request(you can decide what to do on GET/POST separately too).
Model== Make an abstract representation(class of Java) holding all your data that is to be displayed.
View== Here you'll receive the model. In other words, this will be your HTML. You can use JSP helpers to customize the view, the basic idea is to control HOW DATA WILL BE SHOWN TO THE USER(hence the name View). HTML will be automatically generated at run-time and passed to the user.
Again, I say I'm assuming you're very new to web development. Please let me know if I haven't understood your question well. Enjoy coding.