Die wichtige Frage hierbei ist: Wo passiert die Arbeit, am Client oder Server?
Wenn die Operation am Server durchgefuehrt wird ist es ein leichtes den Status mit einem Balken angezeigt zu bekommen. Nehmen wir an wir haben eine lange Operation die am Server laeuft und eine grosze Anzahl an Datensaetzen verarbeitet.
- Code: Select all
public void process()
{
for (IBean row : getStorage().fetchBean(null, null, 0, -1))
{
processRow(row);
}
}
Vom Client aus wird diese normal ueber die Verbindung aufgerufen.
- Code: Select all
getConnection().callAction("process");
Nun hat man hier genau das Problem dass der Aufruf blockiert bis alle Datensaetze abgearbeitet sind. Der erste Schritt ist es den Aufruf asynchron zu machen mit einem Callback.
- Code: Select all
getConnection().callBackAction(this, "onProcessFinished", "process");
Damit hat man nun die Information wann die Operation fertig wird und der Thread wird nicht blockiert. Fehlt nur noch dass man den Status mitbekommt, das geht aber recht einfach. Auf der Server Seite fuegt man die Moeglichkeit ein den Status zu erhalten (der Einfachheit halber nehme ich nun Prozent).
- Code: Select all
private double progress = 0.0d; // Range: 0.0d - 1.0d
public double getProgress()
{
return progress;
}
public void process()
{
progress = 0.0d;
int rowCounter = 0;
List<IBean> rows = getNeededStorage().fetchBean(null, null, 0, -1);
for (IBean row : rows)
{
processRow(row);
progress = ++rowCounter / (double)rows.size();
}
progress = 1.0d;
}
Dann kann man auf der Client Seite den Zustand der Operation abfragen.
- Code: Select all
getConnection().callAction("getProgress");
Der Rest ist Zustandsverwaltung auf der Client Seite.
Falls die Operation auf der Client Seite stattfindet wird die Sache geringfuegig komplizierter da man die Operation vom GUI Thread auslagern muss. Da ist dann darauf zu achten dass man keine Thread uebergreifende Zugriffe auf die Connection, DataBooks oder vergleichbar hat. Ansonsten ist es eine recht geradeaus Implementierung, wichtig ist hierbei wieder die Arbeit aus dem GUI Thread zu verlagern und diesen kann man dann ueber invokeLater ansteuern.
- Code: Select all
public void process(IProgressReport progressReporter)
{
for (int index = 0, rowCount = dataBook.getRowCount(); index < rowCount; index++)
{
dataBook.setSelectedRow(index);
processRow(dataBook);
progressReporter.report(index / (double)rowCount);
}
}
process((pProgress) ->
{
getFactory().invokeLater(() ->
{
progressBar.setProgress(pProgress);
});
});
invokeLater fuehrt den Code dann zum "naechst besten" Zeitpunkt auf dem GUI Thread aus, was in Swing dann ist wenn alle Ereignisse aus der EventQueue abgearbeitet worden sind. Auch hier braucht man natuerlich eine Zustandsverwaltung die richtig reagiert wenn die Operation beginnt, waehrend dessen und auch wenn diese beendet wurde. Also am Anfang das oeffnen des Fensters mit dem Balken und wenn die Operation fertig ist, dass dieses wieder geschlossen wird.
Der Code ist ungetestet, sollte aber eine gutes Bild davon geben wie man etwas derartiges implementiert.