Asynchrones Nachladen in eine Liste

  • Themenstarter Gelöschtes Mitglied 68249
  • Beginndatum
G

Gelöschtes Mitglied 68249

Gast
Hi,
ich soll in unserem Migrationsprojekt jetzt für die Übergangszeit eine Übergangs-Krücke einbauen.
Der Prozess läuft aktuell folgendermaßen ab:
Ich suche mit einer NativeQuery im Altsystem meine Daten zusammen und strukturiere diese schon in die Form der neuen Datenbank, bzw. einigermaßen in die Form der neuen Objekte.
Dann übergebe ich diese List<Object[]> an einen Migrationshandler, welcher dann Feld für Feld aus dem ObjectArray die Werte holt, diese bei Bedarf in das Format des neuen Objekts bringt und dann per Setter in das Objekt schreibt.
Aus Performancegründen persistieren wir aktuell auch nicht mehr, weil die Objekte sowieso nicht in der neuen Datenbank gepflegt werden.

Eine relativ große Suche dauert für Schritt 1 ca. 11 Sekunden und liefert etwas um die 23.000 Datensätze. Flaschenhals ist dann die Migration, hier kann man pro Satz knapp unter einer Sekunde rechnen, weswegen ich das aktuell einfach auf X-Datensätze begrenze.

Jetzt war die Idee die Migration Paketweise zu machen. Also ich habe die komplette Liste mit den ObjektArrays aus der Suche und migriere 25 Sätze davon. Das entspricht im Frontend einer Seite der Tabelle. Dann muss ich das Frontend aktualisieren. Danach werden die nächsten 25 Ergebnisse in die Liste geschrieben und das Frontend wieder aktualisiert, usw.

Nur ich weiß nicht, wie ich das machen soll. Frontend ist aktuell Primefaces 13.
Der Frontend-Handler sieht aktuell auszugsweise so aus:
Java:
@Named
@ViewScoped
public class ProIpsSearchHandler implements Serializable {
    private List<SearchableEntity> searchResults;
    public void search() {
        List<SearchTerm> searchTerms = new ArrayList<>();
        searchTerms.add(new SearchTerm(selectedSearchfield1, selectedSearchOperator1, searchString1, selectedSearchState1));
        searchResults = selectedInterface.searchEntity(searchTerms, 500);
}

Und die Suchfunktion so:
Java:
@ApplicationScoped
public class OldWindowsOsFactory implements Serializable {
    public List<SearchableEntity> searchEntity(List<SearchTerm> searchTerms, Integer maxRecordsAllowed) {

        StringBuilder searchTermAddition = new StringBuilder();
        
        for(SearchTerm term : searchTerms) {
            //Suchbedingung zusammenbauen
        }
        
        String sql = MigSelectSql.SELECT_WINDOWS_OS.replace("%%SEARCH_TERM_ADDITION%%", searchTermAddition.toString());
        Query query = em.createNativeQuery(sql);
        List<Object[]> results = query.getResultList();
        
        if(results.size() > maxRecordsAllowed) {
            results = results.subList(0, maxRecordsAllowed);
        }
        
        List<WindowsOsData> windowsOsList = new ArrayList<>();
        
        try {
            windowsOsList = migWindowsOs.migrateList(results, false);
        } catch(Exception e) {
            LOG.error("Fehler beim holen alter Daten", e);
        }
        
        return new ArrayList<>(windowsOsList);
    }
}

Also theoretisch muss ich OldWindowsOsFactory Runnable implementieren lassen?
Und dann muss die search() im Frontend permanent prüfen, ob die Suche (Migration) fertig ist und alle x Prüfeinheiten könnte sie sich neue Werte holen, oder?
Oder kann ich aus einer Runnable auch pushen, dass es Werte gibt?
Aber das alles aktualisiert mir ja nicht automatisch das Frontend?
 

Oneixee5

Top Contributor
Eine relativ große Suche dauert für Schritt 1 ca. 11 Sekunden und liefert etwas um die 23.000 Datensätze. Flaschenhals ist dann die Migration, hier kann man pro Satz knapp unter einer Sekunde rechnen, weswegen ich das aktuell einfach auf X-Datensätze begrenze.
Was ist dass denn für eine Hardware? Vermutlich eine Dampfmaschine! Aber mal ernsthaft da stimmt was nicht mit der Abfrage: 11 Sekunden für die winzige Datenmenge, dass geht nicht, auch nicht bei 1000 Spalten. Ich würde als erstes die Abfrage auf ein kartesisches Produkt prüfen.
 
G

Gelöschtes Mitglied 68249

Gast
Aber mal ernsthaft da stimmt was nicht mit der Abfrage: 11 Sekunden für die winzige Datenmenge, dass geht nicht, auch nicht bei 1000 Spalten
Die Diskussion möchte ich nicht mehr auf machen, es sind fünf Spalten in der Tabelle und die Datensätze sind vertikal mit einem Index über drei Spalten. Das Select besteht aus 26 left joins und die Datenbank ist einfach Schrott. Hauptgrund für den Umzug in ein ordentliches relationales Datenmodell.


Ließt sich ganz sinnvoll. Muss ich morgen mal ausprobieren.
 

mihe7

Top Contributor
Die Befürchtung habe ich auch, hab gestern schon mal das GIT von Primefaces durchsucht und nichts mehr von den Anotationen gefunden.
Das macht aber nichts, denn wie gesagt: es gibt den Spaß auch in JSF (ab Java EE 8). Und wenn alle Stricke reißen, kann man das auch direkt auf JavaScript-Basis umsetzen, ggf. zusammen mit einem p:remoteCommand.
 
G

Gelöschtes Mitglied 68249

Gast
So, seit 90 Minuten wieder da und muss erstmal feststellen, dass ich die Update-Philosophie der Firma nicht mag.
also
<p:socket> ist aus Primefaces raus, hab es mir aber nicht angeschaut, seit wann.
<f:websocket> ist in Jakarte EE 10 dazu gekommen, wir sind aber aktuell auf Jakarte EE 9 in der Firma und 10 ist für nächstes Jahr vorgesehen.

Also ich muss jetzt wohl ein Zwischending zwischen den beiden Tutorials finden, wobei ich gerade am Ersatz für die beiden html-Tags am suchen bin.
 
G

Gelöschtes Mitglied 68249

Gast
Also ich hab jetzt mal folgenden Code in meine Anwendung mal Standalone mit eingebaut:

Aber auch mit anderen Tests komme ich scheinbar immer an dasselbe Problem:
Scheinbar stimmt mein Endpoint nicht.

Also in der Klasse wird folgendes definiert:
Java:
@ServerEndpoint(value = "/stocks", decoders = {JSONTextDecoder.class}, encoders = {JSONTextEncoder.class})
public class StockExchangeEndpoint {

    private static Set<Session> sessions = new HashSet<>();

    public static void broadcastMessage(JsonObject message) {
        for (Session session : sessions) {
            try {
                session.getBasicRemote().sendObject(message);
            } catch (IOException | EncodeException e) {
                e.printStackTrace();
            }
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("WebSocket opened: " + session.getId());
        sessions.add(session);
    }

    @OnMessage
    public void onMessage(JsonObject message, Session session) {
        System.out.println("Stock information received: " + message + " from " + session.getId());
        try {
            session.getBasicRemote().sendObject(message);
        } catch (IOException | EncodeException e) {
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("WebSocket error for " + session.getId() + " " + throwable.getMessage());
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("WebSocket closed for " + session.getId() + " with reason " + closeReason.getCloseCode());
        sessions.remove(session);
    }
}

und in der XHTML wird folgendes aufgerufen:
HTML:
<script language="javascript" type="text/javascript">
    const webSocket = new WebSocket('ws://localhost:39516/atc/stocks');

    webSocket.onerror = function (event) {
        onError(event)
    };
    webSocket.onopen = function (event) {
        onOpen(event)
    };
    webSocket.onmessage = function (event) {
        onMessage(event)
    };

    function onMessage(event) {
        const eventPayload = JSON.parse(event.data);
        document.getElementById('stockInformation').innerHTML += `<tr><td>${eventPayload.stock}</td><td>${eventPayload.price} $</td></tr>`;
    }

    function onOpen(event) {
        document.getElementById('connectionMessage').innerHTML = 'Connection established';
    }

    function onError(event) {
        alert('An error occurred:' + event.data);
    }

    function send() {
        const payload = {
            'stock': document.getElementById('stockName').value,
            'price': document.getElementById('stockPrice').value
        };

        webSocket.send(JSON.stringify(payload));
    }
</script>
Wenn ich die Seite aufrufe lande ich direkt in function onError(event) den Websocket habe ich angepasst:
meine Anwendung läuft auf HTTP-Port 39516 und im context-root: atc.
Das müsste doch dann genau so passen, oder übersehe ich etwas?

Der Error ist übrigens unknown, aber beim googlen stand, das wäre normal an der Stelle.
 
Zuletzt bearbeitet von einem Moderator:

mihe7

Top Contributor
Der Endpoint sieht okay aus. Was sagt denn das Browser-Log/der Netzwerk-Reiter? Evtl. mag der Browser den Port nicht. Ansonsten würde ich serverseitig erst einmal ohne Decoder/Encoder testen, sprich einfach den String durchreichen.
 
G

Gelöschtes Mitglied 68249

Gast
Okay, hab gestern noch weiter daran, ich nenn das jetzt mal "rumgepfuscht", aus dem Beispiel hatte ich die Klasse StockExchangeNotifier nicht mitgenommen, weil die immer zu Fehlern beim Serverstart geführt hat, keine Ahnung, ob die überhaupt gebraucht wird. Jedenfalls habe ich die wieder eingebaut und mir die Fehler im Log angeschaut. Scheinbar liegt es daran, dass ich nur Interfaces von Websocket verwende und keine Implementierungen, selbiges dann scheinbar noch mit Jakarta.Json, deswegen habe ich die beiden Dependencies mit aufgenommen:
XML:
<dependency>
    <groupId>org.glassfish.tyrus.bundles</groupId>
    <artifactId>tyrus-standalone-client</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.parsson</groupId>
    <artifactId>parsson</artifactId>
</dependency>
Jetzt fährt der Server zwar weiter hoch, es kommt aber in der Klasse zu folgendem Fehler:
Code:
[err] jakarta.websocket.DeploymentException: Connection failed.
[err]     at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientSocket._connect(GrizzlyClientSocket.java:413)
[err]     at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientSocket$1.call(GrizzlyClientSocket.java:212)
[err]     at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientSocket$1.call(GrizzlyClientSocket.java:208)
[err]     at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientSocket.connect(GrizzlyClientSocket.java:226)
[err]     at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientContainer.openClientSocket(GrizzlyClientContainer.java:72)
[err]     at org.glassfish.tyrus.client.ClientManager$3$1.run(ClientManager.java:647)
[err]     at org.glassfish.tyrus.client.ClientManager$3.run(ClientManager.java:696)
[err]     at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
[err]     at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
[err]     at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
[err]     at org.glassfish.tyrus.client.ClientManager$SameThreadExecutorService.execute(ClientManager.java:849)
[err]     at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:123)
[err]     at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:493)
[err]     at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:299)
[err]     at intern.atc.backend.debug.blog.StockExchangeNotifier.init(StockExchangeNotifier.java:30)
[err]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[err]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[err]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[err]     at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[err]     at com.ibm.ejs.container.interceptors.InterceptorProxy.invokeInterceptor(InterceptorProxy.java:202)
[err]     at [internal classes]
[err]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[err]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[err]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[err]     at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[err]     at com.ibm.ejs.container.interceptors.InterceptorProxy.invokeInterceptor(InterceptorProxy.java:191)
[err]     at [internal classes]
[err]     at org.jboss.weld.module.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:81)
[err]     at com.ibm.ws.cdi.ejb.interceptor.WeldSessionBeanInterceptorWrapper.aroundInvoke(WeldSessionBeanInterceptorWrapper.java:60)
[err]     at [internal classes]
[err]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[err]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[err]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[err]     at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[err]     at com.ibm.ejs.container.interceptors.InterceptorProxy.invokeInterceptor(InterceptorProxy.java:191)
[err]     at [internal classes]
[err]     at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
[err]     at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
[err]     at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
[err]     at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:280)
[err]     at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
[err]     at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
[err]     at java.base/java.lang.Thread.run(Thread.java:857)
[err] Caused by:
[err] java.net.ConnectException: Connection refused: no further information
[err]     at java.base/sun.nio.ch.Net.pollConnect(Native Method)
[err]     at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672)
[err]     at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:946)
[err]     at org.glassfish.grizzly.nio.transport.TCPNIOConnectorHandler.onConnectedAsync(TCPNIOConnectorHandler.java:175)
[err]     at org.glassfish.grizzly.nio.transport.TCPNIOConnectorHandler$1.connected(TCPNIOConnectorHandler.java:132)
[err]     at org.glassfish.grizzly.nio.transport.TCPNIOConnection.onConnect(TCPNIOConnection.java:220)
[err]     at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:502)
[err]     at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:82)
[err]     at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:83)
[err]     at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.executeIoEvent(WorkerThreadIOStrategy.java:73)
[err]     at org.glassfish.grizzly.strategies.AbstractIOStrategy.executeIoEvent(AbstractIOStrategy.java:66)
[err]     at org.glassfish.grizzly.nio.SelectorRunner.iterateKeyEvents(SelectorRunner.java:381)
[err]     at org.glassfish.grizzly.nio.SelectorRunner.iterateKeys(SelectorRunner.java:353)
[err]     at org.glassfish.grizzly.nio.SelectorRunner.doSelect(SelectorRunner.java:319)
[err]     at org.glassfish.grizzly.nio.SelectorRunner.run(SelectorRunner.java:248)
[err]     at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:535)
[err]     at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:515)
[err]     ... 1 more
Also die erste Frage ist: "Brauche ich diese Klasse überhaupt für das Beispiel?"
Und wenn ja, wie finde ich heraus, woran der Fehler liegt.

Ansonsten würde ich serverseitig erst einmal ohne Decoder/Encoder testen, sprich einfach den String durchreichen.
Ich habe wirklich nach einem richtig einfachen Beispiel gesucht, aber irgendwie stolpere ich nur über Beispiele, bei denen Encoder und Decoder total wichtig sind. Aber auch das schaue ich mir gleich noch genauer an.

Und, vielleicht auch interessant, im Serverlog erscheint die Fehlermeldung: [WARNUNG ] SRVE0190E: Datei nicht gefunden: /stocks wenn ich auf die xhtml-Seite gehe.
 
Zuletzt bearbeitet von einem Moderator:
G

Gelöschtes Mitglied 68249

Gast
Hach, das wird ein produktiver Tag, das spüre ich schon.
Bin jetzt wirklich einfach Back To The Roots und habe einen Basic Endpoint gebaut:
Java:
import jakarta.websocket.OnMessage;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@ServerEndpoint(value = "/basicEndpoint")
public class BasicEndpoint {
    private static final Logger LOG = LogManager.getLogger(BasicEndpoint.class);
    @OnMessage
    public void onMessage(Session session, String message){
        LOG.info("Websocket meldet " + message);
    }
}
Und teste den jetzt mit Insomnium, aber da erhalte ich einen 404 und im Serverlog steht immer
Code:
[WARNUNG ] SRVE0190E: Datei nicht gefunden: /basicEndpoint/

Also irgendwie ist der nicht richtig registriert, muss ich in der web.xml noch etwas anpassen? Da steht schon einiges drin. Ich habe mal gelesen, dass Leute vorgeschlagen haben die web.xml einfach mal zu löschen und dann würde alles gehen, ist aber bei einem bestehen Projekt wenig zielführend.
 
G

Gelöschtes Mitglied 68249

Gast
So, mein Basic funktioniert jetzt. Nach einem kurzen Gespräch mit meinem Debug-Batman haben wir mal in die Serverfaetures geschaut und da hat Websockets-2.0 gefehlt. Teste jetzt auf jeden Fall mal den ganzen anderen Kram jetzt.
 
G

Gelöschtes Mitglied 68249

Gast
Jetzt hänge ich an der tatsächlichen Verwendung der Sache. Ich habe aktuell eine Bean für mein Frontend. Darin ist ein Objekt, welches die Suchanfrage für das Frontend sammelt. Injected in die Bean ist die Such-Bean (bzw. alle Suchbeans @Inject private Instance<SearchableEntityFactoryInterface> searchFactories;).
Und die Suche wiederum füllt eine private List<SearchableEntity> searchResults; und die wird in der Tabelle angezeigt.

wie verbinde ich denn jetzt die beiden Mechanismen? Wenn der User auf suchen klickt und damit der Websocket angesprochen werden würde, dann müsste ich theoretisch die Suchanfrage als JSON an den Websocket geben und in diesem dann in @OnMessage asynchron die Suche starten? Und der Asynchrone Job müsste mir dann jedes Mal melden, dass er an einem bestimmten Punkt angekommen ist und damit müsste ich dann eine Funktion auslösen, die an die Session sendet. Aber was dann gesendet wird ist ja keine Suchanfrage, sondern ein Suchergebnis, also wären Encoder und Decoder für unterschiedliche Typen?

Kann man also Websocket und Bean mischen? Also so dass ich ihm sagen kann, dass der User beim klick auf Suchen einen Websocket öffnet, damit ich die Session habe und ihm dann an bestimmten Momenten in der Asynchronen Suche einfach an die Session einen Aktualisieren-Trigger schicke, der der Tabelle einfach sagt, dass sie ein Refresh braucht?
 

Oneixee5

Top Contributor
Ich habe das damals so gelöst: Eine Bean kümmert sich um die Registrierung der Websocket-Clients und ist auch ein EventObserver. Andere Beans arbeiten asynchron irgend etwas ab und lösen je nach Bearbeitungsschritt ein Event aus. Der EventObserver kümmert sich dann um die Übermitllung an die Clients und/oder wirft die aus der Liste der Websocket-Clients falls diese nicht mehr erreichbar sind. Die Client-Liste muss natürlich statisch und threadsicher sein.
 

mihe7

Top Contributor
@DaBe1812 der Websocket dient eigentlich nur als "Rückkanal". Heißt: die Seite öffnet einen WebSocket, der JSF-Button startet die Suche, die Seite wird via WebSocket über Änderungen informiert (s. dazu die Ausführungen von Oneixee5) und reagiert darauf. Im Fall einer dataTable würde sich wohl ein update anbieten (p:remoteCommand mit update auf die Table via JavaScript aufrufen).
 
G

Gelöschtes Mitglied 68249

Gast
Ich habe jetzt aktuell eine Lösung, die einigermaßen funktioniert. Das Backend bleibt eine Bean und Websocket habe ich erstmal ausgebaut. Für die Suche habe ich folgende Methoden:
Java:
@Resource
private ManagedExecutorService managedExecutorService;

public void startSearch() {
    if(searchRunning) {
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Fehler", "Es läuft noch eine Suche");
        FacesContext.getCurrentInstance().addMessage(null, msg);
        return;
    }
    if(selectedInterface == null) {
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Fehler", "Es wurde keine Entität ausgewählt");
        FacesContext.getCurrentInstance().addMessage(null, msg);
        return;
    }
    searchResults = new ArrayList<>();
    searchProgress = 0;
    gesamtSuchergebnis = 0;
    anzahlMigriert = 0;
    List<SearchTerm> searchTerms = SearchTerm.makeSearchTermList(userSearchCriteria);
    columns = selectedInterface.getResultFields(username);

    searchRunning = true;
    managedExecutorService.submit(() -> runSearch(searchTerms));
}

private void runSearch(List<SearchTerm> searchTerms) {
    try {
        List<Object[]> rawData = selectedInterface.getRawData(searchTerms,maxRecordsAllowed);
        gesamtSuchergebnis = rawData.size();
        int batchSize = 25;

        for(int i = 0; i < gesamtSuchergebnis; i += batchSize) {
            int end = Math.min(i + batchSize, gesamtSuchergebnis);
            List<Object[]> batch = rawData.subList(i,end);

            List<SearchableEntity> converted = selectedInterface.migrate(batch);

            searchResults.addAll(converted);
            searchProgress = (int) (((double) end / gesamtSuchergebnis) * 100);
            anzahlMigriert = searchResults.size();
        }

        searchRunning = false;
    } catch(Exception e) {
        LOG.error("Fehler bei der Suche", e);
        searchRunning = false;
    }
}

Mein logistisches Problem war, dass ich vorher die Suche und die Migration in einem gemacht habe, ich hatte gar nicht darüber nachgedacht die beiden Schritte zu trennen.

Das Frontend aktualisiere ich jetzt mit einem Poll:
HTML:
    <h:panelGrid columns="3">
        <p:commandButton value="Suchen" action="#{searchHandler.startSearch}" icon="pi pi-search"
                         update="searchResultTable :datenpflegeForm:msgs warnPnl" id="searchBtn" disabled="#{searchHandler.searchRunning}"/>

    </h:panelGrid>

    <h:panelGroup id="searchResultTable">
        <p:progressBar id="searchPrgs" value="#{searchHandler.searchProgress}"
                       labelTemplate="{value}% #{searchHandler.anzahlMigriert}/#{searchHandler.gesamtSuchergebnis}"
                       styleClass="mt-3" global="false" interval="500" rendered="#{searchHandler.searchRunning}" />

        <p:dataTable var="result" value="#{searchHandler.searchResults}" rows="25" paginator="true" paginatorPosition="both"
                paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
                currentPageReportTemplate="{startRecord}-{endRecord} von {totalRecords} Datensätzen"
                rowsPerPageTemplate="25,50,{ShowAll|'Alle'}">
            <p:columns value="#{searchHandler.columns}" var="column" headerText="#{column.displayName}"
                sortBy="#{result[column.propertyName]}" filterBy="#{result[column.propertyName]}">
                <h:outputText value="#{result[column.propertyName]}" rendered="#{column.dataType eq 'string'}" />
                <h:outputText value="#{result[column.propertyName].displayName}" rendered="#{column.dataType eq 'lookup'}" />
                <h:outputText value="#{result[column.propertyName]}" rendered="#{column.dataType eq 'integer'}" />
                <h:outputText value="#{result[column.propertyName]}" rendered="#{column.dataType eq 'date'}">
                    <f:convertDateTime type="localDate" pattern="dd.MM.yyyy" />
                </h:outputText>
                <p:tag styleClass="p-mr-2" severity="success" value="Ja" rendered="#{column.dataType eq 'boolean' and result[column.propertyName]}" style="width: 75;"/>
                <p:tag styleClass="p-mr-2" severity="danger" value="Nein" rendered="#{column.dataType eq 'boolean' and !result[column.propertyName]}" style="width: 75;"/>
            </p:columns>
            <p:column headerText="Details" exportable="false">
                <p:commandLink value="Details anzeigen" action="#{dataExplorerHandler.showDetails(result)}"
                     update=":datenpflegeForm" oncomplete="PF('pipsTabView').select(getCounter())" />
            </p:column>
        </p:dataTable>
    </h:panelGroup>

    <p:poll interval="2" listener="#{searchHandler.updateProgress}" update="searchResultTable" onactivated="console.log('Poll started')"/>
Schönheitsfehler an der Lösung ist, dass ich jetzt einfach so alle 2 Sekunden aktualisiere, auch wenn vielleicht noch kein neues Paket abgearbeitet ist, oder vielleicht schon 3 Neue da sind. Wenn ich in der Tabelle im Spaltenfilter etwas suchen möchte, dann nimmt er mir alle 2 Sekunden den Cursor weg. Darum werde ich den Poll wohl mit einem rendered anpassen, damit er nach der Suche aufhört zu pollen.

Oder würde jetzt diese Lösung auch mit Websocket funktionieren? Nur dass ich mir eben beim Start die Session merken müsste, damit ich dann an den Client melden kann, dass er die Tabelle aktualisieren soll?
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J Asynchrones Servlet löst NullPointerException aus Allgemeines EE 5
B Java mail API - möchte nur eine gewisse Anzahl von Emails in die Liste holen Allgemeines EE 3
B eine vom Admin hochgeladene csv -Datei in der Datatable auch von jedem User sichtbar Allgemeines EE 0
OnDemand Programm starten, wenn eine Aufgabe erledigt Allgemeines EE 1
X Konsolenausgabe einer java klasse in eine jsp umleiten Allgemeines EE 7
T Wie kann ich eine große Datenmenge vorhalten, damit ich seitens Frontend darauf zugreifen kann? Allgemeines EE 17
R Wie eine stateful session bean erneut "aufgreifen" Allgemeines EE 22
D JSF h:panelgrid - eine reihe mit zusätzlicher spalte Allgemeines EE 6
S Wie am besten eine Authentifzierung einbauen? Allgemeines EE 7
B Problem beim einbinden einer CSS in eine JSP Allgemeines EE 8
slawaweis CMS Unterbau für eine Web 2.0 Anwendung Allgemeines EE 4
M Wie erhällt eine MessageDrivenBean Nachrichten aus einer Queue ? Wer Pollt da gegen die DB? Allgemeines EE 3
MQue include einer jsp in eine andere Allgemeines EE 4
D Wann genau eine Middleware Allgemeines EE 8
2 JSTL Tags für eine Bean? Allgemeines EE 4
S Session in eine andere Anwendung übergeben Allgemeines EE 2
D Frage zum Verlassen eine JSF-Eingabefeldes Allgemeines EE 6
S Struts: zwei JSP's nutzen eine Action Allgemeines EE 5
J Rechnername auf dem eine J2EE läuft Allgemeines EE 10
P Eine Frage zum Thema Applikationsaufbau Allgemeines EE 3
H Eine Datenbank - 1 Datenmodell - 2 Anwendungsumgebungen Allgemeines EE 2
E HTTP-GET// -->Eine URL aufrufen, aber nicht dahin navigie Allgemeines EE 2
H Eine kurze Verständnisfrage zum Tomcat Allgemeines EE 2
W Eine Form an einen fremden Server schicken. Allgemeines EE 3
G WebApp (mit Tomcat) Wie kann meine Klasse eine Datei laden? Allgemeines EE 7
E Eine Art Thread.sleep() in JSTL? Allgemeines EE 4
M wie sieht eine ejb-jar.xml aus ? Allgemeines EE 8
T eine web anwendung bereitstellen ? Allgemeines EE 5
N Einbindung einer Bean in eine JSP (Tomcat-Server 5.5.x) Allgemeines EE 2
G StackTrace in eine TEXTAREA bringen Allgemeines EE 4
W Woraus baut man eine Super-Business-Anwendung? Allgemeines EE 5
B Besondere Ländereinstellungen für eine TomcatApp Allgemeines EE 2
TRunKX Werteübergabe von einer *.jsp in eine *.java ohne struts Allgemeines EE 4
G Application Server! Gibt es eine grundsätzliche Architektur? Allgemeines EE 9
B EJB --- Eine Modeerscheinung? Allgemeines EE 14
X Mit JSP eine Datenbankabfrage durch führen. Allgemeines EE 13
Y Eine neue Seite mit Servlet öfnnen Allgemeines EE 9
A mit JavaMail eine html mail versenden? Allgemeines EE 4

Ähnliche Java Themen

Neue Themen


Oben