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.
Related
I have 10 health check URLs which are simply get service
I am hitting them in a loop like below
for(int i=0;i<10;i++){
Response response = given().when().relaxedHttpsValidation().get(url[i]);
list.add(response);
}
return list;
Now the problem is it hits API in series and waiting for a response for all, I just want to hit all API in parallel but combine the result, I tried using threads but unable to get an idea on how to combine the response in case of multi-threading
If I am reading your question right I believe you want to make parallel calls and combine the results, and in that case I would suggest you to make use of TestNG. I had a similar requirement in the past and this link helped me out
Here's a sample code
public class Parallel {
#DataProvider(parallel = true)
public Object[] getURL() {
return new Object[] { "https://reqres.in/api/users/1", "https://reqres.in/api/users/2",
"https://reqres.in/api/users/3", "https://reqres.in/api/users/4", "https://reqres.in/api/users/5",
"https://reqres.in/api/users/6" };
}
ArrayList<String> original = new ArrayList<String>();
#Test(dataProvider = "getURL")
public void stack(String url) {
Response response = given().when().get(url);
JsonPath js = response.jsonPath();
String email = js.getString("data.email");
original.add(js.getString("data.email"));
}
#AfterTest
public void simple() {
System.out.println("List : " + original);
}
}
Just remove (parallel = true) to see how it works sequentially. I have extracted the email field from the response using JSONPath and added to the list
Don't forget to update the POM
Thank you for your quick response i just want to share now how i achieved it
List responseList = new ArrayList();
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
exec.submit(new Runnable() {
public void run() {
String response = executeServiceCall(urlArray[i]);
responseList.add(response);
}
});
} exec.shutdown();
try {
exec.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
LOGGER.error(e.toString());
}
LOGGER.info("response list is " + responseList)
I've made an app which gets data from different urls. I used a for loop to get data from different urls using jsoup lib. Now this takes a lot of time like 5 sec.
First get data from url1 and then 2 and then 3..
This is what taking the time I think.
So I want to know whether we can get data from different urls at the same time(Multiple threads) or not?
public class Title extends AsyncTask <String, Void, Void> {
String url,ver;
Bitmap mIcon11 = null;
ArrayList<App> appsList = new ArrayList<>();
#Override
protected Void doInBackground(String ... strings) {
try {
for (String string : strings) {
Document document = Jsoup.connect(string).get();
Elements a = document.select("div.AppCont");
Elements b = a.select("article");
Elements c = b.select("div.ImgDiv");
Elements d = c.select("img");
url = d.attr("src");
InputStream in = new URL(url).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
ver = b.get(0).text();
String z = string.replace("https://a2zapk.com/History/", "");
z = z.replace("/", "");
PackageInfo pi = getApplicationContext().getPackageManager().getPackageInfo((z), PackageManager.GET_META_DATA);
String versionName = pi.versionName;
ver = ver + " (Installed Version: " +versionName + ")";
appsList.add(new App(ver, mIcon11));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
add(appsList);
}
You absolutely can, but managing the threading for these tasks can get tricky. I would recommend using Rxava to prepare a separate observable for each site you'd like to fetch data from. Then use either merge or mergeDelayError to merge results into a single Observable that you can subscribe to on the main thread to update your UI.
Check out RxJavaAndroid for help subscribing to these updates on Android's main thread.
You'll want to be familiar with 4 core pieces of RxJava:
What you're doing - In your case, this is fetching data from a server
What thread pool is running this task - I'd recommend Schedulers.io() which is a pool set aside specifically for IO tasks like fetching data.
What thread pool you'll be observing results on - AndroidSchedulers.mainThread() is what you'll want here
What to do with the results - Update some UI, etc.
This would look something like the following using RxJava (in Kotlin)
// What you're doing
Observable.fromCallable {
listOfApps = parseAppsList(Jsoup.connect("server1.host.com"))
return#fromCallable listOfApps
}
// Where you're doing it
.subscribeOn(Schedulers.io())
// Where you're observing results
.observeOn(AndroidSchedulers.mainThread())
// What you're doing with those results
.subscribe({ apps ->
appsList.addAll(apps)
}, { exception ->
// Show an error message
})
To fetch multiple results simultaneously and add them as each finishes, your code would look something like this:
val fromServer1 = Observable.fromCallable {
listOfApps = parseAppsList(Jsoup.connect("server1.host.com"))
return#fromCallable listOfApps
}
.subscribeOn(Schedulers.io())
val fromServer2 = Observable.fromCallable {
listOfApps = parseAppsList(Jsoup.connect("server2.host.com"))
return#fromCallable listOfApps
}
.subscribeOn(Schedulers.io())
Observable.merge(fromServer1, fromServer2)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ apps ->
// Note that this callback will be called once per server
appsList.addAll(apps)
}, { exception ->
// Show an error message
})
i've done a rest web service that gives me some contact information like numbers, age ... i get all this information in this function
public static void getRest(String search) {
if(search.equals("")){
json="http://localhost:8080/com.vogella.jersey.first/rest/jsonServices/print/";
} else {
json="http://localhost:8080/com.vogella.jersey.first/rest/jsonServices/print/"+search;
}
ConnectionRequest req = new ConnectionRequest() {
#Override
protected void postResponse() {
}
#Override
protected void readResponse(InputStream input) throws IOException {
JSONParser p = new JSONParser();
Map<String, Object> h = p.parseJSON(new InputStreamReader(input));
ArrayList object=new ArrayList();
for (Entry<String, Object> entry : h.entrySet()) {
object = (ArrayList) entry.getValue();
int i=object.size();
}
for(int i=0; i<object.size();i++){
LinkedHashMap s= (LinkedHashMap) object.get(i);
Risultati.add(s);
}
}
};
req.setUrl(json);
req.setPost(false);
req.addRequestHeader("Accept", "application/json");
InfiniteProgress prog = new InfiniteProgress();
Dialog dlg = prog.showInifiniteBlocking();
req.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueue(req);
Risultati is an attribute of the class: ArrayList<LinkedHashMap> Risultati;
the problem is that when i call the function getRest("") in this way:
getRest("");
Label contatto=null;
for(int j=0;j<Risultati.size();j++){
LinkedHashMap s=Risultati.get(j);
String nome=(String) s.get("firstName");
String cognome=(String) s.get("lastName");
String numero =(String) s.get("numero");
contatto=new Label(nome+" "+cognome+" "+numero);
}
hi.addComponent(contatto);
it turns that Risultati is null, if i comment the for cycle i notice that the inner function readResponse is executed after...i don't know what i'm doing wrong
I think the point is that you're calling NetworkManager.getInstance().addToQueue(req). According to it's documentation, it will add a connection request (the one you've just created) to a queue. After the connection request is added to the queue, it returns, meaning the request may or may not have been executed by that time.
You have to options to deal with this. In my opinion, the best way would be to update the user interface after the request has completed, as described in the "File System, Storage, Network & Parsing" chapter of the CodeOne manual:
req.addResponseListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
NetworkEvent e = (NetworkEvent)ev;
// ... process the response
}
});
Alternatively, you could replace the call to addToQueue(req) with addToQueueAndWait(req). The latter method waits until the request is processed in the queue. The downside of the latter approach is that your user interface may freeze while the request is being processed, because the UI thread is blocked on the network I/O.
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.