# Java Polling



## Kiloui (11. Juni 2012)

Hey,
ich habe eine Funktion die nichts weiter tut als einen String zurückzuliefern.

```
public String getStatus(){
        return Status;
    }
```


Dies soll sie jedoch erst dann tun, sobald eine bestimmte _boolean_-Variable "check" den Wert _true_ annimmt.

Bisher habe nur die Idee das mit einer Endlosschleife zu lösen:

```
public String getStatus(){
    while(!check){
        } 
        return Status;
        }
    }
```


Was sieht eine vernünftige Lösung für das Problem aus ?


----------



## vfl_freak (11. Juni 2012)

Moin,

wie sieht eine vernünftige Beschreibung aus?
Was ist "status" ?
Was ist "check" ?
Wo werden sie gesetzt/gefüllt etc. .....

Gruß
Klaus


----------



## Kiloui (11. Juni 2012)

Die Anwendung ist zu groß um hier genau zu beschreiben was wann wie passiert. Aber:

"Status" ist wie gesagt ein String.  Der Inhalt ist nicht relevant...

Die boolean-Variable "check" wird auf true gesetzt sobald eine gewisse Funktion ihre Arbeit abgeschlossen hat...

Und aufgerufen wird "getStatus()" von einer JSP....ein Client (browser) stellt eine Anfrage an eine JSP...die JSP ruft die JavaMethode (aus einer JavaBean) auf ....der client soll dann so lange auf eine Antwort warten bis diese verfügbar ist.  (long polling


___________________________________________________-

Also dann nochmal etwas genauer:


Ich habe eine Datenbank.

Und eine Methode die ausgeführt wird sobald sich in der Datenbank etwas ändert. Nennen wir diese Methode "checkDB()".    "checkDB()" liest ne ganze Menge Daten aus der Datenbank aus und verarbeitet die dann erstmal.   Sobald das ganze abgeschlossen wird "check" auf true gesetzt.


Ein Client soll die Daten erhalten sobald sich in der Datenbank was geändert hat UND  "checkDB()"  mit seiner Arbeit fertig ist. 

Hierzu stellt der Client eine Anfrage an die JSP. Wenn die Daten aber noch nicht verarbeitet wurden beziehungsweise sich in der Datenbank noch nichts geändert hat soll der client keine leere antwort bekommen und wiedholt die Anfrage stellen müssen....stattdessen soll serverseitig gewartet werden bis sich in der Datenbank was geändert hat UND  "checkDB()"  mit seiner Arbeit fertig ist und das Ergebnis dann als Ausgabe über die JSP geliefert werden.


----------



## Funresort (11. Juni 2012)

public String getStatus(){
    while(!check){
Thread.sleep(5);
        } 
        return Status;
        }
    }

musste die exceptions noch abfangen


----------



## Kiloui (11. Juni 2012)

Wenn ich mit nem Thread.sleep die ausführung der while schleife "kurz" stoppe...wird der rest der java Anwendung dann noch ausgeführt ? Also laufen andere Methoden dann noch parallel weiter und rechnen munter weiter oder wird deren ausführung  durch diese hässliche while-Schleife + sleep dann auch unterbrochen/verzögert  ?


----------



## Funresort (11. Juni 2012)

Dachte du führst das in einem externen Thread aus, wenn nicht mach doch nen externen Thread rein, der solang wartet.


----------



## ByeBye 154279 (11. Juni 2012)

Btw:
http://www.java-forum.org/allgemeine-java-themen/137527-java-polling.html

mfg
bo


----------



## Kiloui (11. Juni 2012)

Hast du einen Link bzw Beispielcode ?


----------



## Funresort (11. Juni 2012)

http://docs.oracle.com/javase/7/docs/api/


----------



## Thomas Darimont (11. Juni 2012)

Hallo,

hier mal ein kleines einfaches Beispiel zu Polling von mehreren Verarbeitungsstati gleichzeitig.

Hierbei verwende ich einfachstes JavaScript und HTML - Das Polling wird (in Old-School manier) über IFrame-reloads realisiert - Im Prinzip verwende ich hier die Technik JSONP (JSON with Padding, siehe: http://en.wikipedia.org/wiki/JSONP) - dabei wird bei der Anfrage der Name einer JavaScript Funktion an den Server mitgegeben , welche dann vom Server wieder in der Antwort als Aufruf mit "Ergebnisargumeten" an den Client-zurückgeschickt wird. 

Das Beispiel bietet Unterstützung für das Tracken mehrerer Verarbeitungsstati  sowie das "Abbrechen" einer serverseitigen Verarbeitung vom Client aus.

Das sollte eigentlich alles beinhalten was du brauchst 

Unser Service Servlet mit Unterstützung für Statusmeldungen und Task-Cancellation:

```
package de.tutorials.app.web.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
@WebServlet(name = "service", urlPatterns = "/service")
public class ServiceServlet extends HttpServlet {

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		//TODO for the sake of brevity ...
		//normally one should store the processing state somewhere else...
		getServletContext().setAttribute("processingState",new ConcurrentHashMap<String, Future<String>>());
	}
	

	@SuppressWarnings("unchecked")
	private ConcurrentMap<String, Future<String>> getProcessingStateMap() {
		return (ConcurrentMap<String, Future<String>>) getServletContext().getAttribute("processingState");
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		String requestIds = req.getParameter("rids");
		String operation = req.getParameter("op");
		String callback = req.getParameter("cb");
		String clientId = req.getParameter("cid");
		
		//TODO validate parameters
		
		if ("statusUpdate".equals(operation)) {
			computeStatusUpdate(resp, requestIds, callback, clientId);
		} else if ("startProcessing".equals(operation)) {
			String requestId = requestIds; // we expect just a single requestId;
			startNewTask(requestId, clientId);
		} else if ("cancelTask".equals(operation)) {
			String requestId = requestIds; // we expect just a single requestId;
			cancelTask(resp, requestId, callback, clientId);
		}
	}

	private void cancelTask(HttpServletResponse resp, String requestId, String callback, String clientId) throws IOException {
		Future<String> future = getProcessingStateMap().get(requestId);
		if (future != null && !future.isDone()) {
			log("Cancelling Task request: " + requestId);
			future.cancel(true);
			getProcessingStateMap().remove(requestId);
			writeStatusResponse(resp.getWriter(), requestId, "cancelled", callback,false);
		}
	}

	private void startNewTask(String requestId, String clientId) {
		log("Starting new Task request: " + requestId + " sent by: " + clientId);
		ExecutorService executor = Executors.newSingleThreadExecutor();
		getProcessingStateMap().put(requestId,executor.submit(newTaskWithRadomDurationMax(20, TimeUnit.SECONDS, requestId)));
		executor.shutdown();
	}

	private void computeStatusUpdate(HttpServletResponse resp, String requestIds, String callback, String clientId) throws IOException {
		if (requestIds == null || requestIds.length() == 0) {
			writeStatusResponse(resp.getWriter(), "bad request", "unknown", callback, false);
			return;
		}

		for (String requestId : requestIds.trim().split(";")) {

			Future<String> future = getProcessingStateMap().get(requestId);

			StringWriter s = new StringWriter();
			PrintWriter out = new PrintWriter(s);

			if (future == null) {
				writeStatusResponse(out, requestId, "unknown", callback,false);
			} else {
				log("Checking for Task request: " + requestId + " done: " + future.isDone()+ " sent by: " + clientId);
				if (future.isDone()) {
					getProcessingStateMap().remove(requestId);
					writeStatusResponse(out, requestId, "completed", callback,true);
				} else {
					writeStatusResponse(out, requestId, "running", callback,true);
				}
			}
			resp.getWriter().println(s.toString());
		}
	}

	private Callable<String> newTaskWithRadomDurationMax(final int amount, final TimeUnit timeUnit, final String requestId) {
		return new Callable<String>() {
			@Override
			public String call() throws Exception {
				log("Task request: " + requestId + " completed in Thread: "
						+ Thread.currentThread());

				int millisToSleepPerIteration = 1000;
				
				long n = timeUnit.toMillis(amount) / millisToSleepPerIteration ; // divide the waiting
															// time in 1 Second
															// intervals, so
															// that we can
															// simulate
															// progress...
				for (int i = 0; i < n; i++) {
					TimeUnit.MILLISECONDS.sleep(millisToSleepPerIteration);
					log("Processing Task request: " + requestId
							+ " completed in Thread: " + Thread.currentThread());
				}
				return requestId;
			}
		};
	}

	private void writeStatusResponse(PrintWriter out, String requestId, String status, String callback, boolean cancellable) throws IOException {
		out.println("<script type='text/javascript'>parent." + callback + "('" + requestId + "','" + status + "'," + cancellable + ");</script>");
	}
}
```

Unsere JSP die das Polling und Message-Passing über IFrames durchführt:
iframe_polling_example.jsp:

```
<!DOCTYPE html>
<%@page import="java.util.UUID"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>IFrame Polling Example</title>

<style type="text/css">
.cancelled {
	background-color: yellow;
}

</style>

<script type="text/javascript">
	var clientId = "<%=UUID.randomUUID()%>";
	var requestSequenceNo = 0;
	var requests = {};

	function getRequestSender() {
		//TODO cache the iframe element
		var messageSenderIFrame = document.getElementById("requestSender");
		return messageSenderIFrame;
	};

	function getStatusUpdater() {
		//TODO cache the iframe element
		var messageSenderIFrame = document.getElementById("statusUpdater");
		return messageSenderIFrame;
	};

	function onStatusUpdate(requestId, status, cancellable) {
		requests[requestId] = status;

		var cancelledOrCompleted = false;
		if (requests[requestId] == "completed" || requests[requestId] == "cancelled") {
			delete requests[requestId];
			cancelledOrCompleted = true;
		}

		document.getElementById("status").innerHTML += "<div class='" 
				+ (status == "cancelled" ? "cancelled" : "") + "'>" 
				+ new Date().toUTCString()
				+ " Request: "
				+ requestId
				+ " Status: "
				+ status
				+ (cancellable && !cancelledOrCompleted ? " <a href='#' onclick='cancelTask(\""+ requestId + "\")'>Cancel Request</a><br/>" : "")
				//+ " <br/>"
				+ "</div>";
	};

	function requestStatusUpdate() {
		if (requestStatusUpdate.running) { //to prevent interleaving update requests... 
			return;
		}
		requestStatusUpdate.running = true;
		var requestIds = "";
		for ( var requestId in requests) {
			requestIds += requestId + ";";
		}
		if (requestIds.length > 0) {
			var statusUpdater = getStatusUpdater(); 
			statusUpdater.src = "service?op=statusUpdate&rids="
					+ requestIds + "&cb=onStatusUpdate&v="
					+ new Date().getTime() + "&cid=" + clientId;
		}
		window.setTimeout("requestStatusUpdate()", 5000);
		requestStatusUpdate.running = false;
	};
	
	function cancelTask(requestId){
		getRequestSender().src = "service?op=cancelTask&rids=" + requestId + "&v=" + new Date().getTime() + "&cb=onStatusUpdate&cid=" + clientId;
	};

	function startTask() {
		var requestId = clientId + "-" + (requestSequenceNo++); 
		onStatusUpdate(requestId, "started", true);
		
		getRequestSender().src = "service?op=startProcessing&rids=" + requestId
				+ "&v=" + new Date().getTime() + "&cid=" + clientId;
		window.setTimeout("requestStatusUpdate()", 500);
	};
	
	function clearStatusMessages(){
		document.getElementById("status").innerHTML = "";
	};
</script>
</head>
<body>
	<a href="#" onclick="startTask()">Start new Task</a>
	<a href="#" onclick="clearStatusMessages()">Clear Status Messages</a>
	<iframe id="statusUpdater" width="0" height="0" style="visibility: hidden"></iframe>
	<iframe id="requestSender" width="0" height="0" style="visibility: hidden"></iframe>
	<div id="status"></div>
</body>
</html>
```

//Edit: getestet in Chrome und IE 9

Gruß Tom


----------



## slowfly (12. Juni 2012)

Mein Senf:
Ich würde Thread.sleep in einer Serverumgebung tunlichst vermeiden. Ist da einmal die DB langsam und der Client refreshed seinen Browser (was User immer machen, wenn eine Seite nicht kommt -> aber der Server wird dadurch nicht schneller) hast du bald und gerne mal deine "maxServerThreads" erreicht und der Applikationsserver fliegt dir quasi um die Ohren.
Und wenn es trotzdem sein muss, keine Endlosschleife reinprogrammieren. Wir sind bei uns teilweise auch gezwungen, solche sleeps zu coden; Ich empfehle, da einen "Maximale Anzahl Versuche" reinzucoden. Wird der letzte Versuch durchgeführt, ohne entsprechendes Resultat -> PanicException (die haben wir bei uns teilweise wirklich ;P)

Was es neuerdings auch gibt sind WebSockets -> Da wird vom Client zum Server eine bidirektionale TCP/IP Verbindung aufgebaut. Somit kann dann auch der Server dem Client sagen, dass er fertig ist. Ist aber nach meinem Wissensstand als noch bisschen "experimental", vor Allem was Browser und Java-Applikationsserver angelangt (mit Jetty und Aurora-Firefox funktioniert's bei mir)- und produktiv haben wir es nie eingesetzt, kann ich somit nur erwähnen, und nicht empfehlen ;P

Gruss
slowy


----------



## Funresort (12. Juni 2012)

oder er gibt seinem Clienten noch nen Server und dem Server noch einen Clienten, so das der Server über den Clienten dem Clienten über dessen Server mitteilen kann das er fertig ist


----------



## Thomas Darimont (12. Juni 2012)

Hallo,



> ...
> Mein Senf:
> Ich würde Thread.sleep in einer Serverumgebung tunlichst vermeiden. Ist da einmal die DB langsam und der Client refreshed seinen Browser (was User immer machen, wenn eine Seite nicht kommt -> aber der Server wird dadurch nicht schneller) hast du bald und gerne mal deine "maxServerThreads" erreicht und der Applikationsserver fliegt dir quasi um die Ohren.
> ...


Nur mal so nebenbei, falls das auf mein Beispiel bezogen ist ;-) - ich muss ja irgendwie "Arbeit" simulieren, deshalb hab ich mal ein paar sleeps eingebaut - generell sollte man natürlich kein nicht verwaltetes Threading in einer JEE (Web) Anwendung verwenden - ist ja sonst nicht Spec-Konform... 



> ...
> Was es neuerdings auch gibt sind WebSockets -> Da wird vom Client zum Server eine bidirektionale TCP/IP Verbindung aufgebaut. Somit kann dann auch der Server dem Client sagen, dass er fertig ist.
> ..


Klar das ist natürlich auch eine Möglichkeit und die wird wahrscheinlich in nicht allzu ferner Zukunft immer mehr an Bedeutung gewinnen - dazu braucht man aber auch erst mal einen Servlet Container der das kann (... kann man auch selbst nachrüsten...), bzw. einen Browser der das Unterstützt (es gibt hier Plugins/Extensions jedoch müssen die auch erstmal "erlaubt" sein ;-) )

Gruß Tom


----------



## slowfly (12. Juni 2012)

Ich habe nichts gegen Thread.sleep, solange es mir meine Applikationsserver nicht kaputtmacht *g* - also HTTP-ThreadPool hat keine Threads mehr vor. (Bin ja Entwickler und "Infrastrukturmanager" oder wie man das nennen will, in einem, und in solchen Situationen haue ich gerne mal auf die Finger, haha).

Als ich mich vor ca. einem halben Jahr oder etwas länger mit den WebSockets auseinander gesetzt habe, war der Jetty nach wie vor der einzige, der das so implementiert hatte, dass es zumindest mal smoke-test mässig funktionierte - kA was passiert, wenn der da 500 permanente Connections hat, zum Beispiel. Und als Browser war das auch nur vom Firefox im Aurorachannel supported (und Chrome, I think,..)

item...
schönen Feierabend =) ... oder vielleicht noch nicht, regnet aus Kübeln

slowy


----------

