Java Polling

Kiloui

Mitglied
Hey,
ich habe eine Funktion die nichts weiter tut als einen String zurückzuliefern.
Code:
    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:
Code:
    public String getStatus(){
    while(!check){
        } 
        return Status;
        }
    }


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

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

Gruß
Klaus
 
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.
 
Zuletzt bearbeitet:
public String getStatus(){
while(!check){
Thread.sleep(5);
}
return Status;
}
}

musste die exceptions noch abfangen
 
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 ?
 
Dachte du führst das in einem externen Thread aus, wenn nicht mach doch nen externen Thread rein, der solang wartet.
 
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:
Java:
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:
HTML:
<!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
 
Zurück