package com.fitbank.bpmserver; import java.sql.Timestamp; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; import org.hibernate.LockMode; import org.hibernate.SQLQuery; import org.jbpm.api.ProcessInstance; import org.jbpm.api.ProcessInstanceQuery; import com.fitbank.bpm.client.BPMProcessor; import com.fitbank.bpm.client.BPMProperties; import com.fitbank.bpmserver.ServiceBpm; import com.fitbank.common.ApplicationDates; import com.fitbank.common.BeanManager; import com.fitbank.common.exception.FitbankException; import com.fitbank.common.Helper; import com.fitbank.common.conectivity.HbSession; import com.fitbank.common.hb.UtilHB; import com.fitbank.dto.management.Detail; import com.fitbank.dto.management.Field; import com.fitbank.hb.persistence.gene.Tflowinstance; public class TimeoutManager extends ServiceBpm { public TimeoutManager() { this.setName("TimeoutManager"); } private long getTime(String pVal) { String time = pVal.replaceAll("minutes", " 60").replaceAll("minutos", " 60").replaceAll("hours", "3600") .replaceAll("horas", "3600"); long ltime = 1; String[] vals = time.split(" "); for (String val : vals) { val = val.trim(); try { ltime *= Long.valueOf(val); } catch (NumberFormatException e) { continue; } } return ltime * 1000; } @Override public void execute() { if (!BPMProperties.getConfig().getBoolean("fit.bpm.process")) { return; } LOGGER.debug("Ejecutando TimeoutManager para BPM cada " + this.getPeriod() + " minutos"); Helper.setSession(HbSession.getInstance().openSession()); this.manageFlows(); Helper.closeSession(); LOGGER.debug("Finalizado el timeout Manager para BPM"); } private void manageFlows() { BPMProcessor cli = new BPMProcessor(); ProcessInstanceQuery qry = cli.getExecutionService().createProcessInstanceQuery(); List lpi = qry.list(); for (ProcessInstance pi : lpi) { try { if (pi.isEnded()) { continue; } LOGGER.debug("Manejando el Flujo " + pi.getId()); BPMProcessor c = BPMProcessor.findProcessInstanceById(pi.getId()); if (c == null) { LOGGER.warn("Flujo " + pi.getId() + " ya no existe en la base de conocimientos"); continue; } this.manageTimeout(c, pi.getId()); Thread.sleep(100); } catch (Exception e) { LOGGER.error("Error desconocido", e); } } } private String findRealId(BPMProcessor c, String activity) { String sub = c.getPid(); BPMProcessor a = BPMProcessor.findProcessInstanceById(sub); if (a == null) { LOGGER.warn("Flujo " + sub + " ya no existe en la base de conocimientos"); return sub; } String act = activity; String aux = null; do { aux = a.findExecutionId(act); if (aux != null) { sub = aux; a = BPMProcessor.findProcessInstanceById(sub); if (a != null) { act = a.findActualStates().iterator().next(); } } } while (aux != null); return sub; } public Tflowinstance findInstance(String pID) { UtilHB util = new UtilHB("from com.fitbank.hb.persistence.gene.Tflowinstance o where o.nombreinstancia=:pid" + " and o.finalizado=:ended"); util.setString("pid", pID); util.setString("ended", "0"); return (Tflowinstance) util.getObject(); } private void timeoutHistory(Detail detail, BPMProcessor c) { Integer max = obtainParameterNumber("MAX_NOTIFICACIONES", detail.getCompany()); String userNotify = detail.findFieldByNameCreate("_USER_NOTIFY") .getStringValue(); String dataToJSON = detail.findFieldByNameCreate("_AUTH_HISTORY") .getStringValue(); JSONObject jsonHistoryTree = new JSONObject(); int count = 0; if (dataToJSON != null) { jsonHistoryTree = (JSONObject) JSONSerializer.toJSON(dataToJSON); } if (userNotify != null && jsonHistoryTree.get(userNotify) == null) { jsonHistoryTree.put(userNotify, count); } if (userNotify != null) { count = jsonHistoryTree.getInt(userNotify); if (count <= max) { ++count; } jsonHistoryTree.put(userNotify, count); LOGGER.debug("History TimeoutManager " + jsonHistoryTree.toString() + " -pid-" + c.getPid()); c.setVariable("detail", detail); detail.findFieldByNameCreate("_AUTH_HISTORY").setValue( jsonHistoryTree.toString()); } } private boolean validateTimeout(Detail detail, String time, String pid, String sub) { Field fTime = detail.findFieldByNameCreate("_TIME_AUTH"); if (fTime.getValue() != null) { time = fTime.getStringValue(); } Object val = detail.findFieldByNameCreate("_NOTIFY_TIME").getValue(); LOGGER.debug("_TIME_AUTH: " + time + "; _NOTIFY_TIME: " + val); if (val == null) { return false; } long ltime = this.getTime(time); long last = (BeanManager.convertObject(val, Timestamp.class)).getTime(); long now = ApplicationDates.getDBTimestamp().getTime(); LOGGER.debug("Timeout Flow: " + pid + " " + sub + " " + ltime + " " + last + " " + new Timestamp(last) + " " + (now - last)); if (ltime > (now - last)) { return false; } return true; } private String findActivity(Tflowinstance ins, BPMProcessor c, String pid) { List s = c.findActualStates(); LOGGER.debug("Actividad: FitBank = " + ins.getActividadactual() + "; Bpm = " + s); boolean valid = false; String activity = ""; for (String val : s) { if (ins.getActividadactual().indexOf(val) > -1) { valid = true; activity = val; break; } } LOGGER.debug("El flujo " + pid + " tiene un estado " + (valid ? "" : "in") + "valido"); if (!valid) { return null; } return activity; } private void manageTimeout(BPMProcessor c, String pid) throws Exception { Helper.beginTransaction(); try { Thread.sleep(100); Tflowinstance ins = this.findInstance(pid); if (ins == null) { return; } else { LOGGER.debug("Registro en TINSTANCIAFLUJO encontrado: " + ins); } String activity = this.findActivity(ins, c, pid); if (activity == null) { return; } String sub = this.findRealId(c, activity); if (sub == null) { return; } else { LOGGER.debug("ProcessID #" + pid + " para la actividad " + activity + " encontrado: " + sub); } c = BPMProcessor.findProcessInstanceById(sub); if (c != null) { Set varNames = c.getVariableNames(); LOGGER.debug("Variables encontradas en PID#" + sub + ": " + varNames); if (!varNames.contains("timeAuth") || !varNames.contains("detail")) { return; } String time = (String) c.getVariable("timeAuth"); Detail detail = (Detail) c.getVariable("detail"); if (detail == null || !this.validateTimeout(detail, time, pid, sub)) { return; } this.timeoutHistory(detail, c); BPMProcessor c1 = BPMProcessor.findProcessInstanceById(c.getPid()); c1.sendSign("TIMEOUT"); } Helper.commitTransaction(); } catch (Exception e) { LOGGER.error("Excepcion al procesar el flujo, haciendo rollback", e); try { Helper.rollbackTransaction(); } catch (Exception e1) { LOGGER.error("No se pudo hacer rollback", e1); } throw e; } } private Integer obtainParameterNumber(String name, Integer company) { //FIXME: Método copiado desde la clase ParameterHelper en negocio-general //para quitar dependencia a negocio de BPM. Incidencia #6490. String sqlNumero = "select tcps.VALORNUMERICO from " + "tcompaniaparametrossistema tcps where tcps.CPARAMETROSISTEMA=:name " + "and tcps.CPERSONA_COMPANIA=:company and tcps.fhasta=:fhasta"; SQLQuery sqlPNumero; sqlPNumero = Helper.getSession().createSQLQuery(sqlNumero); sqlPNumero.setString("name", name); sqlPNumero.setInteger("company", company); sqlPNumero.setTimestamp("fhasta", ApplicationDates.DEFAULT_EXPIRY_TIMESTAMP); Object num = sqlPNumero.uniqueResult(); if (num == null) { throw new FitbankException("GEN666", "NO SE ENCUENTRA PARAMETRIZADO {0} EN LA BASE DE DATOS", name); } return (Integer) BeanManager.convertObject(num, Integer.class); } @Override public void shutdown() { } @Override public Integer getPeriod() { return BPMProperties.getConfig().getInt("fit.bpm.timeouts.period"); } }