Unterschiede Timer und Oberfläche?

DaBe1812

Bekanntes Mitglied
Hallo zusammen,

ich habe hier ein seltsames Phänomen. Wir haben einen Prozess, der sehr viele Dinge tut und ganz viel auf der Datenbank ablegen soll.
Deswegen soll das ganze einmal im Monat über einen Timer gestartet werden. Sieht aktuell so aus:
Java:
@Singleton
@DependsOn("ServerCache")
public class TimedFunctions {

    private final static Logger LOG = LogManager.getLogger(TimedFunctions.class);

    @Inject
    transient private MasterQueryTimer masterQuery;   

//    @Schedule(dayOfMonth = "1", hour = "10", minute="11",  persistent = false) //TODO anpassen/
    @Schedule(hour="*", minute="20",  persistent = false) //TODO anpassen
    private void timerMasterQuery(final Timer t) {
        masterQuery.startMasterQuery();
        LOG.info("Master Query Timer beendet.");
    }
}

Die MasterQueryTimer:
Java:
@ApplicationScoped
public class MasterQueryTimer {
public static final Logger LOG = LogManager.getLogger(MasterQueryTimer.class);
    
    @Inject private Provider<MasterQueryController> cntlPrivoder;
    private MasterQueryController cntl;
    
    public void startMasterQuery() {
        try {
            cntl=cntlPrivoder.get();
            cntl.processQuerys();   
        }catch(Exception e ) {
            LOG.error("Fehler beim Performance-Test KV!");
        }           
    }
}

Und der MasterQueryController:
Java:
@Model
public class MasterQueryController implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private final static Logger LOG = LogManager.getLogger(MasterQueryController.class);

    @Inject
    private Provider<OracleModel> oracleProvider;
    @Inject
    private Provider<Db2Model> db2Provider;
    @Inject
    private ResponseTimeInterface responseTimeInterface; //Entität für Abfragen
    @Inject
    private ResponseTimeQueryInterface responseTimeQueryInterface; //Entität für Ergebnisse

    private OracleModel om;
    private Db2Model dm;
    private HashMap<String, String> globaleVariablen;
    private ArrayList<MasterQuery> sqlsMitErsetztenVariablenList;
    private static final ReferenceSystem REFSYSTEM = ReferenceSystem.KV;

    @PostConstruct
    private void init() {
        LOG.info("Init MasterQueryController");
        this.globaleVariablen = new HashMap<>();
        this.sqlsMitErsetztenVariablenList = new ArrayList<>();
        LOG.info("Lesen der KV-ID aus der Tabelle Response_Time_Query.");

    }

    public void processQuerys() {
        LOG.info("Start Performanceabfrage KV Master");
        om = oracleProvider.get();
        dm = db2Provider.get();

        LOG.info("Lesen der Selects für die globalen Variablen aus der Master.");
        List<MasterQuery> globaleQueryList = om.getGlobaleVariablen();

        LOG.info("Lesen aller benötigten Selects, deren Ausführungszeit gemessen werden soll.");
        List<MasterQuery> zuPruefendeSqlsAusMaster = om.getZuPruefendeSqlsAusMaster();

        LOG.info("Holen der Institute aus der Master");
        List<String> institutsList = om.getInstitute();
        if (!institutsList.isEmpty()) {
            for (String einInst : institutsList) {// zb D307
      // Tu ganz viele Dinge
      }

        } else {
            LOG.error("Es sind keine Institute in der Master vorhanden.");
        }
        LOG.info("Performance Master Query ist fertig.");//TODO kann weg message ist weiter oben
    }
}

Wenn der Timer den Prozess startet, dann kommt es irgendwann zu folgender Exception:
Code:
[FEHLER  ] DSRA0304E:  Es ist eine Ausnahme vom Typ XAException eingetreten. Der Inhalt der XAException-Ausnahme und die Details sind wie folgt:
The XA Error is            : -7
The XA Error message is    : Resource manager is unavailable.
The Oracle Error code is   : 17008
The Oracle Error message is: Internal XA Error
The cause is               : java.sql.SQLRecoverableException: Getrennte Verbindung.
[FEHLER  ] DSRA0302E: Es ist eine XAException eingetreten. Der Fehlercode ist XAER_RMFAIL (-7). Die Ausnahme XAErr (-7): Resource manager is unavailable. ORA-17008 SQLErr (0) wurde ausgelöst.

Wenn man allerdings den MasterQueryTimer über einen Button in der Oberfläche startet, dann läuft der Code einwandfrei durch.

Ich habe den Scheduler in verdacht, dass der ein implizites Timeout o.ä. hat, aber ich finde dazu nichts.
 

Oneixee5

Top Contributor
Ich denke man müsste wissen wie dein OracleModel aussieht. Es ist ja offensichtlich ein Verbindungsproblem mit der Oracle-DB.
 

DaBe1812

Bekanntes Mitglied
Moin,
gerne, aber ich glaube das Problem liegt woanders. Das Oracle Model baut eine DB-Verbindung zu einer Oracle Datenbank auf, die nicht zu meinem Server gehört. Die ServerManaged Verbindungen sind in den beiden Klassen ResponseTimeInterface und ResponseTimeQueryInterface und mir fällt gerade auf, dass ich deren Verwendung im Beispiel vergessen habe, dann eben ungekürzt:
Java:
@Model
public class MasterQueryController implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private final static Logger LOG = LogManager.getLogger(MasterQueryController.class);

    @Inject
    private Provider<OracleModel> oracleProvider;
    @Inject
    private Provider<Db2Model> db2Provider;
    @Inject
    private ResponseTimeInterface responseTimeInterface;
    @Inject
    private ResponseTimeQueryInterface responseTimeQueryInterface;

    private OracleModel om;
    private Db2Model dm;
    private HashMap<String, String> globaleVariablen;
    private ArrayList<MasterQuery> sqlsMitErsetztenVariablenList;
    private static final ReferenceSystem REFSYSTEM = ReferenceSystem.KV;

    @PostConstruct
    private void init() {
        LOG.info("Init MasterQueryController");
        this.globaleVariablen = new HashMap<>();
        this.sqlsMitErsetztenVariablenList = new ArrayList<>();
        LOG.info("Lesen der KV-ID aus der Tabelle Response_Time_Query.");

    }

    public void processQuerys() {
//TODO problem mit der datenbankverbindung bei 2 inst und dem objekt => daniel geben bzw abwarten ob es sich mit dem jakarta umbau von allein ergibt
        LOG.info("Start Performanceabfrage KV Master");
        om = oracleProvider.get();
        dm = db2Provider.get();

        LOG.info("Lesen der Selects für die globalen Variablen aus der Master.");
        List<MasterQuery> globaleQueryList = om.getGlobaleVariablen();

        LOG.info("Lesen aller benötigten Selects, deren Ausführungszeit gemessen werden soll.");
        List<MasterQuery> zuPruefendeSqlsAusMaster = om.getZuPruefendeSqlsAusMaster();

        LOG.info("Holen der Institute aus der Master");
        List<String> institutsList = om.getInstitute();
        if (!institutsList.isEmpty()) {
            for (String einInst : institutsList) {// zb D307
                LOG.info("Aktuelles Institut: " + einInst);
                String instNr = einInst.substring(1, 4);
                String regionsKz = einInst.substring(0, 1);
                // Globale Selects auf DB2 ausführen und in der Map globaleVariablen speichern

                LOG.info("Login Host");
                boolean isLoginErfolgreich = dm.login(regionsKz);

                if (isLoginErfolgreich) {
                    LOG.info("Selects der globalen Variablen auf dem DB2 ausführen.");
                    globaleVariablen = dm.fuehreGlobaleSqlsAus(instNr, globaleQueryList);
                    // Schleife über die zu prüfenden DB2-Sqls => aussortieren wenn diese EID= drin
                    // haben und ersetzen der Variablen, falls sie diese drin haben
                    LOG.info("Ersetzen der globalen Variablen in allen zu prüfenden Selects");
                    sqlsMitErsetztenVariablenList = dm.checkMasterSql(zuPruefendeSqlsAusMaster, globaleVariablen);

                    ArrayList<ResponseTime> responseTimeList = new ArrayList<>();
                    LOG.info("Ausführung und Messung der zu prüfenden Sqls im DB2.");
                    for (MasterQuery einSql : sqlsMitErsetztenVariablenList) {
                        ResponseTime responseTime = null;
                        try {
                            Duration dauer = dm.fuehreSqlAus(instNr, einSql);
                            responseTime = new ResponseTime(REFSYSTEM, getId(), Integer.parseInt(instNr), regionsKz,
                                    einSql.getEidVerweis());
                            if (dauer == null) {
                                responseTime.setRespState(QueryTimeResponseState.ERROR);
                                responseTime.setErrMsg(dm.getFehlermeldung());
                                LOG.error("Fehler bei der Performanceabfrage der Master-Querys: "
                                        + dm.getFehlermeldung());
                            } else {
                                responseTime.setDuration(dauer.getNano());

                            }
                            responseTimeList.add(responseTime);
                        } catch (Exception e) {
                            responseTime.setRespState(QueryTimeResponseState.ERROR);
                            responseTime.setErrMsg(e.getMessage());
                            LOG.error("Fehler bei der Performanceabfrage der Master-Querys: ", e);
                        }
                    }
                    dm.logoff();
                    // wegschreiben der Daten in die Datenbank pro Institut
                    LOG.info("Schreiben der Ausführungszeiten in die Datenbank in die Tabelle Response_Times");
                    responseTimeInterface.createRespTimesTrans(responseTimeList);
                    LOG.info("Fertig mit dem Insert der Ausführungszeiten in die DB ");
                }
            }

        } else {
            LOG.error("Es sind keine Institute in der Master vorhanden.");
        }
        LOG.info("Performance Master Query ist fertig.");//TODO kann weg message ist weiter oben
    }

    private ResponseTimeQuery getId() {// hier was ändern wegen der datenbankverbindung=> daniel fraen wie man di db
                                        // verbindung kappt
        // ID aus der tabelle response_time_query holen, da sie zum wegschreiben der
        // duration benötigt wird
        List<ResponseTimeQuery> listKvEintrag = responseTimeQueryInterface.loadByName("KV_MASTER_QUERYS_PERFORMANCE");
        ResponseTimeQuery responseTime = null;
        if (listKvEintrag.size() == 1) {
            responseTime = listKvEintrag.get(0);
        }
        return responseTime;

    }
}

Und die beiden Klassen sind recht überschaubar:
ResponseTimeInterface
Java:
@ApplicationScoped
public class ResponseTimeInterface {

    @Inject EntityManagerProvider emp; //Datenbankverbindung ATC
    
    //Schreiben in die DB für bspw. MasterQuery (KV)
    @Transactional
    public void createRespTimesTrans(ArrayList<ResponseTime> a1) {
        EntityManager em = emp.getEntityManager();
        for (ResponseTime adb1 : a1) {
            em.persist(adb1);
        }
    }
        
}

ResponseTimeQueryInterface
Java:
@ApplicationScoped
public class ResponseTimeQueryInterface {

    @Inject EntityManagerProvider emp;
    
    // nur das Referenzsystem eingeschränkt wird aus der Response_Time_Query Tabelle
    // geholt
    public List<ResponseTimeQuery> loadByName(String name) {
        EntityManager em = emp.getEntityManager();
        TypedQuery<ResponseTimeQuery> query = em.createNamedQuery("ResponseTime.loadByName", ResponseTimeQuery.class);
        List<ResponseTimeQuery> result = query.setParameter("name", name).getResultList();
        return result;
    }

}

Und der EntityManagerProvider ist recht klassisch:
Java:
@RequestScoped
public class EntityManagerProvider {

    @PersistenceContext(unitName = "atcdb")
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }
}
 

DaBe1812

Bekanntes Mitglied
Ich habe die Vermutung, dass der @Schedule implizit irgendwann den Prozess wegen zu lang abbricht, oder der Kontext sich auflöst, o.ä.
Meine Idee zur Lösung war es den Prozess einfach asynchron zu starten, aber irgendwie bekomm ich es nicht mehr hin, irgendwie fehlt ihm ein Kontext.
Was ich bisher gemacht habe:
Im Timer:
Java:
@Singleton
@DependsOn("ServerCache")
public class TimedFunctions {
    private final static Logger LOG = LogManager.getLogger(TimedFunctions.class);
    @Inject transient private MasterQueryTimer masterQuery;   
    @Resource private ManagedExecutorService mes;
    
    @Schedule(hour="*", minute="17",  persistent = false) //TODO anpassen
    private void timerMasterQuery(final Timer t) {
        mes.submit(masterQuery);
//        masterQuery.startMasterQuery();
        LOG.info("Master Query Timer beendet.");
    }
}
Also einen Managed Executor dazu.
Deswegen dann MasterQueryTimer so umgebaut:
Java:
@RequestScoped
public class MasterQueryTimer implements Runnable{
    public static final Logger LOG = LogManager.getLogger(MasterQueryTimer.class);

    @Inject private Provider<MasterQueryController> cntlPrivoder;
    private MasterQueryController cntl;

    public void startMasterQuery() {
        try {
            cntl = cntlPrivoder.get();
            cntl.processQuerys();   
        }catch(Exception e ) {
            LOG.error("Fehler beim Performance-Test KV!");
        }           
    }

    @Override
    public void run() {
        startMasterQuery();
    }
}
In der Zeile "cntl.processQuerys();" knallt es dann aber weil:
Code:
org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
 

DaBe1812

Bekanntes Mitglied
Hi, lange nichts mehr zu dem Thema hier passiert.
Ich habe den Timer jetzt bei mir im System komplett selbst überarbeitet und kein fertiges Framework genommen, weil jedes irgendwo einen Haken gehabt hat.

Jetzt müsste ich den Scheduler von diesem System trennen, damit dieser einfach nur alle 5 Minuten eine andere Klasse "antippt" und diese dann den Rest macht, außerdem möchte ich die Ausführung der Timer Asynchron haben, d.h. sie sollen alle gleichzeitig laufen.

Der Scheduler ist so implementiert:
Java:
@Singleton
@Startup
@DependsOn("DatabaseCheck")
public class JobScheduler {
    @Schedule(hour = "*", minute = "*/5", second = "0", persistent = false)
    public void scheduleJobs() {
        List<JobSchedule> jobSchedules = jobScheduleDAO.getPendingJobs();
        List<Job> jobs = new ArrayList<>();
        List<JobSchedule> doubleJobs = new ArrayList<>();
        
        for (JobSchedule jobSchedule : jobSchedules) {
            if (isJobDue(jobSchedule)) {
                
                Job job = jobSchedule.getJob();
                
                if(jobs.contains(job) && !skipCycles.contains(job.getJobCycle())) {
                    doubleJobs.add(jobSchedule);
                    continue;
                }
                
                jobs.add(jobSchedule.getJob());
                
                jobScheduleDAO.setRunning(jobSchedule);
                
                try {
                    executeJob(jobSchedule.getJob());
                    jobScheduleDAO.setCompleted(jobSchedule);
                } catch (Exception e) {
                    jobScheduleDAO.setError(jobSchedule, e.getMessage());
                }

                if(job.getJobCycle() != JobCycle.ON_DEMAND && job.getJobCycle() != JobCycle.ONCE) {
                    jobScheduleDAO.nextSchedule(jobSchedule, jobSchedule.getJob().calculateNextRun());
                }
            }
        }
        
        jobScheduleDAO.removeAllPending(doubleJobs);
    }
    
    private void executeJob(Job job) {
        jobExecutor.executeJob(job);
    }
}

Aktuell prüft der also alle 5 Minuten, ob ein Job fällig ist und führt diesen dann aus. Der Teil muss erstmal ausgelagert werden, aber irgendwie habe ich eine Blockade es richtig zu machen.

Also eigentlich müsste ich einen Sysout am Anfang und einen am Ende der Funktion haben und die müssten innerhalb von Millisekunden getriggert werden, weil den Rest dazwischen mit den DB Operationen macht ein asynchroner Task.
 

Ähnliche Java Themen

Neue Themen


Oben