Wert aus einem Future raus bekommen

DaBe1812

Bekanntes Mitglied
Hi,
ich bastle gerade mit Futures rum und habe ein kleines Problem. Ich habe eine Page um eine Massenoperation durch zu führen, die gut und gerne mal Minuten lang läuft. Jetzt will ich den User nicht hängen lassen, also erstmal Sanduhr eingeblendet, bis die Operation durch ist. jetzt möchte ich dem User aber auch sagen, bei wie viel Prozent der Prozess gerade ist, und da kommt der Fehler.

Also der Handler sieht so aus:
Java:
@Named("massenabbauhandler")
@ViewScoped
public class MassenabbauHandler implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer inProgress = 0;
    private Massenabbau massenabbau = new Massenabbau();
    private List<String> abbauObjects = new ArrayList<>();
    private String abbauStep = "Bitte laden Sie eine Datei hoch";
    private boolean startBlocked = true;
    private List<String> posCols = new ArrayList<>();
    private boolean error = false;

    @Inject private MassenabbauWorker worker;

    public void startAbbau() throws InterruptedException, ExecutionException {
        setInProgress(1);

        worker.setMassenabbau(massenabbau);
        
        Future<String> workerResult = worker.work();
        
        while(!workerResult.isDone()) {
            Thread.sleep(1000);
            setInProgress(worker.getStatus());
        }

        setInProgress(100);

        abbauStep = workerResult.get();

        this.massenabbau = null;
        
        FacesMessage.Severity severity = FacesMessage.SEVERITY_INFO;
        String summary = "Abbau erfolgreich";
        String detail = "Die Systeme wurden erfolgreich abgebaut";
        
        if(abbauStep.contains("!!ERROR!!")) {
            severity = FacesMessage.SEVERITY_ERROR;
            summary = "Fehler beim Abbau";
            detail = abbauStep.replace("!!ERROR!!", "");
            abbauStep = "FEHLER " + detail;
            error = true;
        }

        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(severity , summary, detail));
        PrimeFaces.current().ajax().update("abbauForm:dataPanel");
    }
}

Der MassenabbauWorker dann so:
Java:
@Stateless
public class MassenabbauWorker {

    private static final Logger LOG = LogManager.getLogger(MassenabbauWorker.class);
    
    private String object;
    private List<Map<String,String>> hostList;
    private String spalte;
    private LocalDate abbauDatum;
    private String ticketNo;

    private double numHosts = 0d;
    private double actHost = 0d;

    @Inject private MassenabbauDbHandler dbHandler;
    
    @Resource private ManagedExecutorService mes;

    public void setMassenabbau(Massenabbau massenabbau) {
        object = massenabbau.getAbbauObject();
        hostList = massenabbau.getSheet().getTableSheetContent();
        spalte = massenabbau.getHostCol();
        abbauDatum = massenabbau.getAbbaudatum();
        ticketNo = massenabbau.getTicket();
        numHosts = hostList.size();
    }

    public Future<String> work() {
        return mes.submit(() -> { return doAbbau();});
    }

    public String doAbbau() {
        LocalDateTime start = LocalDateTime.now();
        String ret = "";
    try {
      tu Zeug und zähl Zeilen
      return "Abbau von " + hostList.size() + " Systemen abgeschlossen in " + durString;
    } catch (Exception e) {
      LOG.error("Fehler beim Abbau: ", e);
      return "!!ERROR!!" + e.getMessage();
    }       
     }
 
    public Integer getStatus() {
        Double prozent = (actHost / numHosts) * 100;
        Integer rounded = Math.round(prozent.floatValue());
        System.out.println("Status: " + actHost + "/" + numHosts + " sind Prozente: " + rounded);
        return (rounded < 1 ? 1 : rounded);
    }
}

Es knallt natürlich in der Schleife, wo ich wissen will, wie viel Prozent durch sind, weil ich auf den worker direkt zugreife und nicht auf das Future Objekt, aber das Future lässt mich ja nicht auf die Funktion zugreifen, oder?
Kann ich irgendwie schauen, wie weit das Future ist?
 

DaBe1812

Bekanntes Mitglied
Mist, gerade runter gefahren. Also an der Stelle (Zeile 26 im Beispiel) sagt er mir, dass es keinen Open RequestScope gibt und er schmiert ab. Wenn du glaubst, dass es genau so gehen müsste, dann guck ich morgen nochmal nach der genauen Meldung und pack sie hier drunter.
 

LimDul

Top Contributor
Das sieht dann aber nach ein Problem mit den CDI-Scopes, dass irgendwo er eine Request-Scoped Bean braucht, die nicht da ist. Kann natürlich mit dem Future Zeig zu tun haben, aber würde ich eher nicht glauben.

Evtl. in der setInProgressMethode? (Wobei er dann schon vorher in Zeile 18 hätte knallen müssen). Seltsam
 

DaBe1812

Bekanntes Mitglied
So, wahrscheinlich kommt der Fehler daher, dass die Worker-Klasse schon abgeschmiert ist, bevor ich das erste mal an einen Zwischenstand kommen möchte. Scheinbar ist der MassenabbauDbHandler das Problem. Sobald der das erste Mal verwendet wird, kommt folgende Exception:
Code:
org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
Die Klasse war @Model und ich habe mal auf @RequestScope geändert. So sieht sie jetzt etwa aus:
Java:
@RequestScoped
public class MassenabbauDbHandler {

    private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd.MM.yyyy");
    
    @PersistenceContext(unitName = "proipsolddb")
    private EntityManager em;

    @SuppressWarnings("unchecked")
    public List<String> getGuids(String hostname, String feldindex) {
        String sql = OldSQLMassenabbau.GETGUID;
        sql = sql.replace(":feldindex", feldindex);
        sql = sql.replace(":hostname", hostname);
        
        Query query = em.createNativeQuery(sql);
        List<String> resultList = query.getResultList();

        return resultList;
    }

    @SuppressWarnings("unchecked")
    public Integer getMaxVersion(String guid) {
        String sql = OldSQLMassenabbau.GETMAXVERSION;
        sql = sql.replace(":guid", guid);

        Query query = em.createNativeQuery(sql);
        List<Object> resultList = query.getResultList();

        Object o = resultList.get(0);
        
        if(o instanceof Number) {
            return ((Number) o).intValue();
        }
        return null;
    }

    @Transactional
    public void setEndedatum(String guid, int version, LocalDate abbauDatum) {
  //Code
    }

    @Transactional
    public void insertNewKatalog(String guid, Integer version, LocalDate abbauDatum) {
  //Code
    }
 
  //... usw.
}
Ich denke da liegt schon das Hauptproblem. Bevor ich auf das Future gewechselt habe, lief das System problemlos.
 

LimDul

Top Contributor
Ich glaube, ich sehe das Problem.

Über den Future startest du einen eigenen Thread. In diesem Thread gibt es keinen Request-Scope, das ist ein Server Thread, der eben nicht einen Request abarbeitet. Das heißt beim Zugriff MassenabbauDbHandler (der als Proxy injected ist) wird versucht den Request zu ermitteln und diesen Request gibt es im Kontext des Threads nicht.

Warum muss die Bean MassenabbauDbHandler Request-Scoped sein? Eigentlich könnte die auch ApplicationScoped sein, oder?
 

DaBe1812

Bekanntes Mitglied
Da bin ich mir immer nicht sicher, wie man das am besten verteilt. Ich hatte letztlich wieder einen Fehler, weil ich Request und View nicht richtig gesetzt hatte.
Also technisch gesehen wird die Bean immer durch den Server verarbeitet. Es gibt nichts in der Bean, was sessionrelevant ist, also wenn 5 sessions auf diese Bean zugreifen macht das keinen Unterschied. Dann würde ich sie ApplicationScoped machen.
Muss ich dann in den anderen Klassen auch noch etwas ändern?
 

DaBe1812

Bekanntes Mitglied
Okay, dann sage ich mal danke dir und der schwarzen CDI-Magie, der Test läuft gerade (ich Depp hab mal 700 Sätze zum Testen genommen facepalm) und bisher keine Ausfälle.
Jetzt werde ich mal in mich gehen und sämtliche anderen dbHandler anschauen, ob die nicht evtl. auch ApplicationScoped sein sollten.
 

DaBe1812

Bekanntes Mitglied
Was mir dabei aber auffällt, ist, dass es jetzt langsamer ist, als früher. Könnte das daran liegen, dass ich alle Transaktionen in eine extra Funktion gepackt habe? Das sieht im Worker etwa so aus:
Java:
List<String> guids = dbHandler.getGuids(hostname, "2020");

if(guids.isEmpty()) continue;
for(String guid : guids) {
    Integer version = dbHandler.getMaxVersion(guid);
    if(version != null) {
        dbHandler.setEndedatum(guid,version,abbauDatum);
        dbHandler.insertNewKatalog(guid,version,abbauDatum);
        dbHandler.insertNewDaten(guid,version);
        dbHandler.insertNewVerweis(guid,version);
        dbHandler.updateLogsysDaten(guid, version, abbauDatum, hostname);
        dbHandler.updateVerweise(guid, version);
        dbHandler.setTicketNo(guid, version, ticketNo, "2018");
    }
}
und im dbHandler dahinter dann so:
Java:
@Transactional
public void setEndedatum(String guid, int version, LocalDate abbauDatum) {
    String sql = OldSQLMassenabbau.SETENDEDATUMKATALOG;
    sql = sql.replace(":endedatum", abbauDatum.format(dtf));
    sql = sql.replace(":guid", guid);
    sql = sql.replace(":version", Integer.toString(version));
    Query query = em.createNativeQuery(sql);
    query.executeUpdate();

    sql = OldSQLMassenabbau.SETENDEDATUMOBJEKTDATEN;
    sql = sql.replace(":endedatum", abbauDatum.format(dtf));
    sql = sql.replace(":guid", guid);
    sql = sql.replace(":version", Integer.toString(version));
    query = em.createNativeQuery(sql);
    query.executeUpdate();
}

@Transactional
public void insertNewKatalog(String guid, Integer version, LocalDate abbauDatum) {
    String sql = OldSQLMassenabbau.INSERTNEWKATALOG;
    sql = sql.replace(":startdatum", abbauDatum.format(dtf));
    sql = sql.replace(":guid", guid);
    sql = sql.replace(":version", Integer.toString(version));
    Query query = em.createNativeQuery(sql);
    query.executeUpdate();
}

@Transactional
public void insertNewDaten(String guid, Integer version) {
    String sql = OldSQLMassenabbau.INSERTNEWOBJEKTDATEN;
    sql = sql.replace(":guid", guid);
    sql = sql.replace(":version", Integer.toString(version));
    Query query = em.createNativeQuery(sql);
    query.executeUpdate();
}

@Transactional
public void insertNewVerweis(String guid, Integer version) {
    String sql = OldSQLMassenabbau.INSERTNEWVERWEISE;
    sql = sql.replace(":guid", guid);
    sql = sql.replace(":version", Integer.toString(version));
    Query query = em.createNativeQuery(sql);
    query.executeUpdate();
}
//Auszugsweise
Ich muss hier aktuell auch noch bei nativeQueries bleiben, weil die Tabellen in der aktuellen Version noch nicht wirklich OOP-tauglich sind
 

LimDul

Top Contributor
Kann gut sein, sind jetzt halt 7 eigene Transaktionen mit Commit pro Zeile. Die eine Transaktion pro Zeile zu verlagern würde schon helfen.
 

DaBe1812

Bekanntes Mitglied
Hm, also hab jetzt mal testweise alles in eine Funktion gepackt, spart 2 Minuten (von 12 auf 10) ist nicht der Geschwindigkeitsvorteil, den ich mir erhofft hatte. Oder lassen sich die Transaktionen batchen? Das hatte ich vorher, da hatte ich die DB-Verbindung aber noch nicht servermanaged, sondern hab mit einer normalen Connection und Prepared Statements gearbeitet.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
S Struts: Wert in einem iterierten Drop-Down Menü selektieren Allgemeines EE 7
J Konvertierungs-Fehler: Wert ''{0}'' für Modell ''{1}'' Allgemeines EE 10
M Wert aus Servlet an normale Klasse übergeben Allgemeines EE 2
E Servlet: Wert aus eingabefeld auslesen funkioniert nicht Allgemeines EE 4
B Wert an JSP übergeben Allgemeines EE 11
B Probleme mit Templates in einem Webprojekt Allgemeines EE 6
P Logging bei einem WebApplication Allgemeines EE 2
J geschützter Bean zugriff mit einem Rich-Client Allgemeines EE 2
K Wie statisch ist static in einem Webserver? Allgemeines EE 5
M Zwei Buttons in einem Servlet Allgemeines EE 2
T Zugriff auf persistentes Set in einem Objekt Allgemeines EE 2
P Einem eingeloggtem Benutzer Rollen dynamisch zuweisen Allgemeines EE 5
M Java Application Server in einem ungesunden Zustand Allgemeines EE 4
R Zugriff auf Managed Bean aus einem Filter Allgemeines EE 2
boxi JSF von einem Bean auf ein anderes Bean zugreifen Allgemeines EE 3
G JBoss aus einem Java-Programm starten Allgemeines EE 11
S JSF - Aktion und Navigation in einem? Allgemeines EE 3
M Aus einem Servlet auf ein anderes zugreifen ? Allgemeines EE 2
O JSP dynamisch aus einem objekt heraus erstellen Allgemeines EE 7
G benutzerverwaltung in einem bestellshop Allgemeines EE 3
M datei liste (jsp,html) von einem Server Allgemeines EE 4
J In einem Bean zugriff auf ein SessionBean? Allgemeines EE 2
F Timer in einem Applicationserver laufen lassen? Allgemeines EE 4
H tomcat - alles umleiten zu einem Servlet Allgemeines EE 40
H Von einem Servlet auf ein anderes zugreifen Allgemeines EE 4
H Lastverteilung in einem Cluster (JBoss) ausschalten Allgemeines EE 4
M Servlet: CSS der JSP wird bei einem forward nicht gefunden? Allgemeines EE 2
flashfactor Logging in einem Session-Bean Allgemeines EE 2
H Beep auf einem WebClient? Allgemeines EE 5
R Wie ermittle ich Zeichenanzahl in einem gemischten String? Allgemeines EE 2

Ähnliche Java Themen

Neue Themen


Oben