diff --git a/cws-core/src/main/java/jpl/cws/core/db/SchedulerDbService.java b/cws-core/src/main/java/jpl/cws/core/db/SchedulerDbService.java index 184a2f5f..adfe79b5 100644 --- a/cws-core/src/main/java/jpl/cws/core/db/SchedulerDbService.java +++ b/cws-core/src/main/java/jpl/cws/core/db/SchedulerDbService.java @@ -24,16 +24,16 @@ /** * Helper / service methods related scheduler database tables. - * + * * @author ghollins * */ public class SchedulerDbService extends DbService implements InitializingBean { private static int externalWorkerNum = 0; private static final Logger log = LoggerFactory.getLogger(SchedulerDbService.class); - + @Autowired private CwsEmailerService cwsEmailerService; - + public static final String PENDING = "pending"; public static final String DISABLED = "disabled"; public static final String FAILED_TO_SCHEDULE = "failedToSchedule"; @@ -44,65 +44,65 @@ public class SchedulerDbService extends DbService implements InitializingBean { public static final String RESOLVED = "resolved"; public static final String FAIL = "fail"; public static final String INCIDENT = "incident"; - + public static final int DEFAULT_WORKER_PROC_DEF_MAX_INSTANCES = 1; - public static final int PROCESSES_PAGE_SIZE = 100; + public static final int PROCESSES_PAGE_SIZE = 50; - public static final String FIND_CLAIMABLE_ROWS_SQL = + public static final String FIND_CLAIMABLE_ROWS_SQL = "SELECT uuid FROM cws_sched_worker_proc_inst " + - "WHERE " + - " status='"+PENDING+"' AND " + - " proc_def_key=? " + - "ORDER BY " + - " priority ASC, " + // lower priorities favored - " created_time ASC " + // older dates (FIFO) favored - "LIMIT ?"; - - public static final String UPDATE_CLAIMABLE_ROW_SQL = + "WHERE " + + " status='"+PENDING+"' AND " + + " proc_def_key=? " + + "ORDER BY " + + " priority ASC, " + // lower priorities favored + " created_time ASC " + // older dates (FIFO) favored + "LIMIT ?"; + + public static final String UPDATE_CLAIMABLE_ROW_SQL = "UPDATE cws_sched_worker_proc_inst " + - "SET " + - " claimed_by_worker=?, " + - " claim_uuid=?, " + - " status='"+CLAIMED_BY_WORKER+"' " + - "WHERE " + - " uuid=? AND claim_uuid IS NULL " + - " AND EXISTS (SELECT * FROM cws_worker WHERE id=? AND status='up')"; - - public static final String INSERT_SCHED_WORKER_PROC_INST_ROW_SQL = + "SET " + + " claimed_by_worker=?, " + + " claim_uuid=?, " + + " status='"+CLAIMED_BY_WORKER+"' " + + "WHERE " + + " uuid=? AND claim_uuid IS NULL " + + " AND EXISTS (SELECT * FROM cws_worker WHERE id=? AND status='up')"; + + public static final String INSERT_SCHED_WORKER_PROC_INST_ROW_SQL = "INSERT INTO cws_sched_worker_proc_inst " + - "(uuid, created_time, updated_time, proc_inst_id, " + - "proc_def_key, proc_business_key, priority, proc_variables, status, error_message, " + - "initiation_key, claimed_by_worker, started_by_worker, last_rejection_worker, num_worker_attempts, claim_uuid) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - + "(uuid, created_time, updated_time, proc_inst_id, " + + "proc_def_key, proc_business_key, priority, proc_variables, status, error_message, " + + "initiation_key, claimed_by_worker, started_by_worker, last_rejection_worker, num_worker_attempts, claim_uuid) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + public static final String PROC_INST_STATUS_SQL = - " IF (PI.END_TIME_ IS NULL, 'running', " + - "IF (AI.ACT_TYPE_ in ('noneEndEvent','endEvent','escalationEndEvent','compensationEndEvent','signalEndEvent','terminateEndEvent') AND " + - "PI.END_TIME_ IS NOT NULL, 'complete', 'fail')) "; + " IF (PI.END_TIME_ IS NULL, 'running', " + + "IF (AI.ACT_TYPE_ in ('noneEndEvent','endEvent','escalationEndEvent','compensationEndEvent','signalEndEvent','terminateEndEvent') AND " + + "PI.END_TIME_ IS NOT NULL, 'complete', 'fail')) "; + - public SchedulerDbService() { log.trace("SchedulerDbService constructor..."); } - + @Override public void afterPropertiesSet() throws Exception { log.trace("jdbcTemplate = "+jdbcTemplate); } - - + + public boolean engineProcessRowExists(String procDefKey) { int numRows = jdbcTemplate.queryForObject( - "SELECT count(*) FROM cws_worker_proc_def " + - "WHERE proc_def_key=?", - new Object[]{procDefKey}, Integer.class); + "SELECT count(*) FROM cws_worker_proc_def " + + "WHERE proc_def_key=?", + new Object[]{procDefKey}, Integer.class); return numRows > 0; } - - + + /** * Inserts a row into the cws_sched_worker_proc_inst table. - * + * */ public void insertSchedEngineProcInstRow(final SchedulerJob schedulerJob) throws Exception { long t0 = System.currentTimeMillis(); @@ -144,32 +144,32 @@ protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQL } } } - - + + public void batchUpdateProcInstRowStatus( Set uuids, String oldStatus, String newStatus) throws Exception { - + log.warn("batch is " + uuids + ", " + uuids.size()); } - - + + /** * Updates a cws_sched_worker_proc_inst row's status, * while ensuring only a valid state transition occurs * (by querying by current/old status). - * + * */ public void updateProcInstRowStatus( - String uuid, - String oldStatus, - String newStatus, - String errorMessage, - boolean clearWorkerInfo) throws Exception { + String uuid, + String oldStatus, + String newStatus, + String errorMessage, + boolean clearWorkerInfo) throws Exception { long t0 = System.currentTimeMillis(); log.trace("uuid="+uuid+": " + oldStatus + "--->" + newStatus +", errorMessage="+errorMessage); - + // Attempt to update the database // There is a slight chance that the process will finish so quickly that // we may need to try several times here, hence the while loop. @@ -178,20 +178,20 @@ public void updateProcInstRowStatus( // to avoid race conditions here int numTries = 0; int numUpdated = 0; - + while (numUpdated == 0 && numTries < 20) { numUpdated = jdbcTemplate.update( - "UPDATE cws_sched_worker_proc_inst " + - "SET status=?, updated_time=?, " + - (clearWorkerInfo ? "claim_uuid = NULL, claimed_by_worker = NULL, started_by_worker = NULL, last_rejection_worker = NULL," : "") + - "error_message=? " + - "WHERE uuid=? AND status=?", - new Object[] {newStatus, - new Timestamp(DateTime.now().getMillis()), - errorMessage, uuid, oldStatus}); + "UPDATE cws_sched_worker_proc_inst " + + "SET status=?, updated_time=?, " + + (clearWorkerInfo ? "claim_uuid = NULL, claimed_by_worker = NULL, started_by_worker = NULL, last_rejection_worker = NULL," : "") + + "error_message=? " + + "WHERE uuid=? AND status=?", + new Object[] {newStatus, + new Timestamp(DateTime.now().getMillis()), + errorMessage, uuid, oldStatus}); if (numUpdated == 0 && ++numTries < 20) { String rowStatus = getProcInstRowStatus(uuid); - + // Workaround for potential Camunda bug. // This bug should now be fixed (as of v7.3.1+), // but it was decided that leaving this code in anyways @@ -203,7 +203,7 @@ public void updateProcInstRowStatus( log.warn("already updated row to '"+COMPLETE+"' status -- workaround for Camunda bug"); return; // don't try to update anymore } - + log.warn("sleeping before trying DB update again..."); Thread.sleep(250); } @@ -212,14 +212,14 @@ public void updateProcInstRowStatus( if (timeTaken > SLOW_WARN_THRESHOLD) { log.warn("updateProcInstRowStatus (cws_sched_worker_proc_inst) took " + timeTaken + " ms!"); } - + if (numUpdated != 1) { - throw new Exception("did not update 1 row, updated "+numUpdated + ". " + + throw new Exception("did not update 1 row, updated "+numUpdated + ". " + "(uuid="+uuid+": " + oldStatus + "--->" + newStatus +", errorMessage="+errorMessage+")"); } } - - + + public int updateProcInstIdAndStartedByWorker( String uuid, String workerId, @@ -227,27 +227,27 @@ public int updateProcInstIdAndStartedByWorker( long t0 = System.currentTimeMillis(); int numUpdated = jdbcTemplate.update( "UPDATE cws_sched_worker_proc_inst " + - "SET started_by_worker=?, proc_inst_id=?, updated_time=? " + - "WHERE uuid=?", + "SET started_by_worker=?, proc_inst_id=?, updated_time=? " + + "WHERE uuid=?", new Object[] { - workerId, - procInstId, - new Timestamp(DateTime.now().getMillis()), - uuid} - ); + workerId, + procInstId, + new Timestamp(DateTime.now().getMillis()), + uuid} + ); long timeTaken = System.currentTimeMillis() - t0; if (timeTaken > SLOW_WARN_THRESHOLD) { log.warn("UPDATE of cws_sched_worker_proc_inst took " + timeTaken + " ms!"); } return numUpdated; } - + /** * Attempt to claim a process start request in the database. - * + * * @param workerProcsList -- attempts to claim rows for the active set of process definition(s) * @return mappings of claimUuids and claimedRowUuids * @@ -274,11 +274,11 @@ public Map> claimHighestPriorityStartReq(String workerId, Ma // for (Map.Entry procs : limitsPerProcs.entrySet()) { rowUuidsPerProcDefKey = jdbcTemplate.queryForList(FIND_CLAIMABLE_ROWS_SQL, String.class, - new Object[] {procs.getKey(), procs.getValue()*2}); + new Object[] {procs.getKey(), procs.getValue()*2}); // get list of uuids using array of procdefkeys IN (keys) unfilteredRowUuids.addAll(rowUuidsPerProcDefKey); } - + Collections.sort(unfilteredRowUuids); for (String id : unfilteredRowUuids) { String procDefKeyString = getProcDefKeyFromUuid(id); @@ -371,7 +371,7 @@ else if (log.isTraceEnabled()) { else { log.trace("no rows claimed by worker: " + workerId); } - + if (numClaimed != claimUuids.size()) { log.error("numUpdated != claimUuids.size()" ); } @@ -382,13 +382,13 @@ else if (log.isTraceEnabled()) { return ret; } - - + + public String getProcInstRowStatus(String uuid) { List> list = jdbcTemplate.queryForList( - "SELECT status FROM cws_sched_worker_proc_inst " + - "WHERE uuid=?", - new Object[] {uuid}); + "SELECT status FROM cws_sched_worker_proc_inst " + + "WHERE uuid=?", + new Object[] {uuid}); if (list != null && !list.isEmpty()) { return list.iterator().next().values().iterator().next().toString(); } @@ -399,8 +399,8 @@ public String getProcInstRowStatus(String uuid) { public int getMaxProcsValueForWorker(String workerId) { return jdbcTemplate.queryForObject( - "SELECT max_num_running_procs FROM cws_worker WHERE id=?", - new Object[] {workerId}, Integer.class); + "SELECT max_num_running_procs FROM cws_worker WHERE id=?", + new Object[] {workerId}, Integer.class); } public int getCountForClaimedProcInstPerKey(String procDefKey, List claimedUuids) { @@ -417,9 +417,9 @@ public String getProcDefKeyFromUuid(String uuid) { public Map getProcInstRow(String uuid) { List> list = jdbcTemplate.queryForList( - "SELECT * FROM cws_sched_worker_proc_inst " + - "WHERE uuid=?", - new Object[] {uuid}); + "SELECT * FROM cws_sched_worker_proc_inst " + + "WHERE uuid=?", + new Object[] {uuid}); if (list.size() != 1) { log.error("unexpected list size: " + list.size() + ", for uuid: " + uuid); } @@ -430,54 +430,54 @@ public Map getProcInstRow(String uuid) { return null; } } - - + + /** - * + * */ public List> getClaimedProcInstRows(List claimUuids) { long t0 = System.currentTimeMillis(); String claimUuidsStr = "'" + StringUtils.join(claimUuids.toArray(), "','") + "'"; List> list = jdbcTemplate.queryForList( - "SELECT * FROM cws_sched_worker_proc_inst " + - "WHERE claim_uuid IN (" + claimUuidsStr + ")"); + "SELECT * FROM cws_sched_worker_proc_inst " + + "WHERE claim_uuid IN (" + claimUuidsStr + ")"); long timeTaken = System.currentTimeMillis() - t0; if (timeTaken > SLOW_WARN_THRESHOLD) { log.warn("SELECT * FROM cws_sched_worker_proc_inst by claim_uuid took " + timeTaken + " ms!"); } - + if (list.size() != claimUuids.size()) { log.error("unexpected claim size: " + list.size() + ", for claim_uuids: " + claimUuidsStr + " (expected " + claimUuids.size() + ")"); } return list; } - + public boolean externalWorkerExists(String workerId) { return jdbcTemplate.queryForObject( - "SELECT count(*) FROM cws_external_worker WHERE id=?", new Object[]{workerId}, Integer.class) > 0; + "SELECT count(*) FROM cws_external_worker WHERE id=?", new Object[]{workerId}, Integer.class) > 0; } - + /** * Create a row (if not already exists) in the database for this engine */ - public String createExternalWorkerRow(String workerId, String hostname) { + public String createExternalWorkerRow(String workerId, String hostname) { if (!externalWorkerExists(workerId)) { log.info("Inserting row into cws_external_worker table..."); - + int numUpdated = 0; int numTries = 0; String workerName = null; while (numTries++ < 10 && numUpdated != 1) { Timestamp tsNow = new Timestamp(DateTime.now().getMillis()); workerName = "ext_worker" + String.format("%1$4s", externalWorkerNum++).replace(' ', '0'); - + try { numUpdated = jdbcTemplate.update( "INSERT INTO cws_external_worker" + - " (id, name, hostname, created_time, last_heartbeat_time) " + - "VALUES (?,?,?,?,?)", + " (id, name, hostname, created_time, last_heartbeat_time) " + + "VALUES (?,?,?,?,?)", new Object[] { workerId, workerName, @@ -487,142 +487,142 @@ public String createExternalWorkerRow(String workerId, String hostname) { }); } catch (DataAccessException e) { - + try { // Could not update database, wait and retry again Thread.sleep((long)(Math.random() * 500.0)); } catch (InterruptedException ex) { - + } } } - + if (numUpdated != 1) { log.error("Could not create external worker row for workerId " + workerId + " !"); } - + return workerName; } - + log.error("Could not create external worker row for workerId " + workerId + " !"); - + return null; } - - + + public int updateExternalWorkerHeartbeat(String workerId) { return jdbcTemplate.update( - "UPDATE cws_external_worker SET last_heartbeat_time = ? WHERE id=?", - new Object[] { new Timestamp(DateTime.now().getMillis()), workerId } + "UPDATE cws_external_worker SET last_heartbeat_time = ? WHERE id=?", + new Object[] { new Timestamp(DateTime.now().getMillis()), workerId } ); } - + public int updateExternalWorkerActiveTopics(String workerId, String activeTopics) { return jdbcTemplate.update( - "UPDATE cws_external_worker SET activeTopics = ? WHERE id=?", - new Object[] { activeTopics, workerId } + "UPDATE cws_external_worker SET activeTopics = ? WHERE id=?", + new Object[] { activeTopics, workerId } ); } - + public int updateExternalWorkerCurrentTopic(String workerId, String currentTopic) { return jdbcTemplate.update( - "UPDATE cws_external_worker SET currentTopic = ? WHERE id=?", - new Object[] { currentTopic, workerId } + "UPDATE cws_external_worker SET currentTopic = ? WHERE id=?", + new Object[] { currentTopic, workerId } ); } - + public int updateExternalWorkerCurrentCommand(String workerId, String currentCommand) { return jdbcTemplate.update( - "UPDATE cws_external_worker SET currentCommand = ? WHERE id=?", - new Object[] { currentCommand, workerId } + "UPDATE cws_external_worker SET currentCommand = ? WHERE id=?", + new Object[] { currentCommand, workerId } ); } - + public int updateExternalWorkerCurrentWorkingDir(String workerId, String currentWorkingDir) { return jdbcTemplate.update( - "UPDATE cws_external_worker SET currentWorkingDir = ? WHERE id=?", - new Object[] { currentWorkingDir, workerId } + "UPDATE cws_external_worker SET currentWorkingDir = ? WHERE id=?", + new Object[] { currentWorkingDir, workerId } ); } - + public List> getWorkers() { return jdbcTemplate.queryForList( - "SELECT * FROM cws_worker ORDER BY name"); + "SELECT * FROM cws_worker ORDER BY name"); } - + public List> getExternalWorkers() { return jdbcTemplate.queryForList( - "SELECT * FROM cws_external_worker ORDER BY name"); + "SELECT * FROM cws_external_worker ORDER BY name"); } - + public List> getWorkersStats() { return jdbcTemplate.queryForList( - "SELECT status, COUNT(*) as cnt FROM cws_worker WHERE cws_install_type != 'console_only' GROUP BY status"); + "SELECT status, COUNT(*) as cnt FROM cws_worker WHERE cws_install_type != 'console_only' GROUP BY status"); } - + public List> getDiskUsage() { return jdbcTemplate.queryForList( - "SELECT id, name, cws_install_type, disk_free_bytes FROM cws_worker"); + "SELECT id, name, cws_install_type, disk_free_bytes FROM cws_worker"); } - + public List> getLogUsage(String workerId) { return jdbcTemplate.queryForList( - "SELECT filename, size_bytes FROM cws_log_usage WHERE worker_id=?", - new Object[] { workerId } - ); + "SELECT filename, size_bytes FROM cws_log_usage WHERE worker_id=?", + new Object[] { workerId } + ); } - + public long getDbSize() throws Exception { List> list = jdbcTemplate.queryForList( "SELECT SUM(data_length + index_length) AS size " + - "FROM information_schema.TABLES " + - "WHERE table_schema = (SELECT DATABASE())"); - + "FROM information_schema.TABLES " + + "WHERE table_schema = (SELECT DATABASE())"); + if (list.size() != 1) { throw new Exception("Could not get database size."); } return Long.parseLong(list.get(0).get("size").toString()); } - - + + /** * Returns the number of "up" Workers. - * + * */ public int getNumUpWorkers() { String query = "SELECT COUNT(*) FROM cws_worker WHERE status = 'up'"; return jdbcTemplate.queryForObject(query, Integer.class); } - - + + /** - * + * */ public List> getWorkerNumRunningProcs() { return jdbcTemplate.queryForList( - "SELECT cws_worker.id, active_count as cnt " + - "FROM cws_worker"); + "SELECT cws_worker.id, active_count as cnt " + + "FROM cws_worker"); } - - + + /** * Gets a list of unresponsive workers. - * + * */ public List> detectDeadWorkers(int thresholdMilliseconds) { try { Timestamp thresholdTimeAgo = new Timestamp(DateTime.now().minusMillis(thresholdMilliseconds).getMillis()); return jdbcTemplate.queryForList("SELECT * FROM cws_worker " + - "WHERE last_heartbeat_time < ? AND status = 'up'", - new Object[] { thresholdTimeAgo }); + "WHERE last_heartbeat_time < ? AND status = 'up'", + new Object[] { thresholdTimeAgo }); } catch (Throwable e) { cwsEmailerService.sendNotificationEmails("CWS Database Error", "Severe Error!\n\nCould not query database for dead workers.\n\nDetails: " + e.getMessage()); log.error("Problem occurred while querying the database for dead workers.", e); - + throw e; } } @@ -646,40 +646,40 @@ public List> detectAbandonedWorkers(int daysToAbandoned) { throw e; } } - - + + /** * Gets a list of unresponsive external workers. - * + * */ public List> detectDeadExternalWorkers(int thresholdMilliseconds) { try { Timestamp thresholdTimeAgo = new Timestamp(DateTime.now().minusMillis(thresholdMilliseconds).getMillis()); return jdbcTemplate.queryForList("SELECT * FROM cws_external_worker " + - "WHERE last_heartbeat_time < ?", - new Object[] { thresholdTimeAgo }); + "WHERE last_heartbeat_time < ?", + new Object[] { thresholdTimeAgo }); } catch (Throwable e) { cwsEmailerService.sendNotificationEmails("CWS Database Error", "Severe Error!\n\nCould not query database for dead external workers.\n\nDetails: " + e.getMessage()); log.error("Problem occurred while querying the database for dead external workers.", e); - + throw e; } } - + /** - * - */ + * + */ public void deleteProcessDefinition(String procDefKey) { - + jdbcTemplate.update( "DELETE FROM cws_worker_proc_def " + - "where proc_def_key=?", + "where proc_def_key=?", new Object[] {procDefKey}); - + jdbcTemplate.update( "DELETE FROM cws_sched_worker_proc_inst " + - "where proc_def_key=?", + "where proc_def_key=?", new Object[] {procDefKey}); } @@ -705,46 +705,46 @@ public void deleteAbandonedWorker(String workerId) { } - + /** - * - */ + * + */ public void deleteDeadExternalWorkers(String workerId) { jdbcTemplate.update( - "DELETE FROM cws_external_worker " + - "where id=?", - new Object[] {workerId}); + "DELETE FROM cws_external_worker " + + "where id=?", + new Object[] {workerId}); } /** - * + * */ public int getWorkerJobExecutorMaxPoolSize(String workerId) { return jdbcTemplate.queryForObject( - "SELECT job_executor_max_pool_size FROM cws_worker WHERE id=?", - new Object[] {workerId}, Integer.class); + "SELECT job_executor_max_pool_size FROM cws_worker WHERE id=?", + new Object[] {workerId}, Integer.class); } - - + + /** - * + * */ public Map getCwsProcessInstanceRowForUuid(String uuid) { return jdbcTemplate.queryForMap( - "SELECT * " + - "FROM cws_sched_worker_proc_inst " + - "WHERE uuid=?", - new Object[] {uuid}); + "SELECT * " + + "FROM cws_sched_worker_proc_inst " + + "WHERE uuid=?", + new Object[] {uuid}); } /** * Updates the job table to set retries, used to retry processes * which have raised an incident and have exhausted their retries - * - * If there is an entry in the external task table with no retries, - * it updates that as well. This allows the process engine to - * gracefully recover from both failed jobs and failed external - * task executions. + * + * If there is an entry in the external task table with no retries, + * it updates that as well. This allows the process engine to + * gracefully recover from both failed jobs and failed external + * task executions. * * Probably can do this in the Camunda API, but this works for now * @@ -761,24 +761,24 @@ public int setRetriesForUuids(List uuids, int retries) { // do this first because otherwise a process could raise an incident while retries // are still being set, and then the new incident would be deleted String query = - "DELETE FROM ACT_RU_INCIDENT " + - "WHERE PROC_INST_ID_ IN " + uuidSqlArray; + "DELETE FROM ACT_RU_INCIDENT " + + "WHERE PROC_INST_ID_ IN " + uuidSqlArray; jdbcTemplate.update(query); String jobQuery = - "UPDATE ACT_RU_JOB " + - "SET RETRIES_ = ? " + - "WHERE RETRIES_ = 0 " + - "AND PROCESS_INSTANCE_ID_ = ? "; + "UPDATE ACT_RU_JOB " + + "SET RETRIES_ = ? " + + "WHERE RETRIES_ = 0 " + + "AND PROCESS_INSTANCE_ID_ = ? "; // Also set retries for this process's external tasks String extTaskQuery = - "UPDATE ACT_RU_EXT_TASK " + - "SET RETRIES_ = ? " + - "WHERE RETRIES_ = 0 " + - "OR RETRIES_ IS NULL " + - "AND PROC_INST_ID_ = ? "; + "UPDATE ACT_RU_EXT_TASK " + + "SET RETRIES_ = ? " + + "WHERE RETRIES_ = 0 " + + "OR RETRIES_ IS NULL " + + "AND PROC_INST_ID_ = ? "; int updated = 0; for(String uuid : uuids) { @@ -839,7 +839,7 @@ public int getFilteredProcessInstancesSize( String statusList, String minDate, String maxDate - ) + ) { List whereObjs = new ArrayList(); if (procInstId != null) { whereObjs.add(procInstId); } @@ -855,48 +855,48 @@ public int getFilteredProcessInstancesSize( List statuses = Arrays.asList(statusList.split(",")); statusClause = buildSanitizedSqlArray(statuses, pattern); } - + log.trace("statusClause = " + statusClause); - + int cwsRowsCount = - jdbcTemplate.queryForObject( - "SELECT COUNT(*) " + - "FROM cws_sched_worker_proc_inst " + - "WHERE " + - (procInstId != null ? "proc_inst_id=? AND " : "") + - (procDefKey != null ? "proc_def_key=? AND " : "") + - (minDate != null ? "created_time >= ? AND " : "") + - (maxDate != null ? "created_time <= ? AND " : "") + - (statusList != null ? "status IN "+statusClause+" AND " : "") + - "proc_inst_id IS NULL ", // don't get any started processes - whereObjs.toArray(), Integer.class); + jdbcTemplate.queryForObject( + "SELECT COUNT(*) " + + "FROM cws_sched_worker_proc_inst " + + "WHERE " + + (procInstId != null ? "proc_inst_id=? AND " : "") + + (procDefKey != null ? "proc_def_key=? AND " : "") + + (minDate != null ? "created_time >= ? AND " : "") + + (maxDate != null ? "created_time <= ? AND " : "") + + (statusList != null ? "status IN "+statusClause+" AND " : "") + + "proc_inst_id IS NULL ", // don't get any started processes + whereObjs.toArray(), Integer.class); // Now add superProcInstId to whereObjs and put at the beginning for SQL query. Only add if contains "real" procInstId if (superProcInstId != null && !superProcInstId.equalsIgnoreCase("NULL")) { whereObjs.add(0, superProcInstId); } String camundaCountQuery = "SELECT COUNT(*) " + - "FROM cws_proc_inst_status " + - "WHERE " + - (superProcInstId != null ? superProcInstId.equalsIgnoreCase("NULL") ? "super_proc_inst_id IS NULL AND " : "super_proc_inst_id=? AND " : "") + - (procInstId != null ? "proc_inst_id=? AND " : "") + - (procDefKey != null ? "proc_def_key=? AND " : "") + - (statusList != null ? "status IN " + statusClause + " AND " : "") + - (minDate != null ? "start_time >= ? AND " : "") + - (maxDate != null ? "start_time <= ? AND " : "") + - " 1=1 "; + "FROM cws_proc_inst_status " + + "WHERE " + + (superProcInstId != null ? superProcInstId.equalsIgnoreCase("NULL") ? "super_proc_inst_id IS NULL AND " : "super_proc_inst_id=? AND " : "") + + (procInstId != null ? "proc_inst_id=? AND " : "") + + (procDefKey != null ? "proc_def_key=? AND " : "") + + (statusList != null ? "status IN " + statusClause + " AND " : "") + + (minDate != null ? "start_time >= ? AND " : "") + + (maxDate != null ? "start_time <= ? AND " : "") + + " 1=1 "; int camundaRowsCount = jdbcTemplate.queryForObject(camundaCountQuery, whereObjs.toArray(), Integer.class); - + log.trace("cwsRowsCount = " + cwsRowsCount + ", camundaRowsCount = " + camundaRowsCount); - + return cwsRowsCount + camundaRowsCount; } - - + + /** * Returns the set of filtered process instances. - * + * */ public List> getFilteredProcessInstances( String superProcInstId, @@ -907,14 +907,20 @@ public List> getFilteredProcessInstances( String maxDate, String dateOrderBy, int page - ) - { + ) + { List whereObjs = new ArrayList(); if (procInstId != null) { whereObjs.add(procInstId); } if (procDefKey != null) { whereObjs.add(procDefKey); } if (minDate != null) { whereObjs.add(minDate); } if (maxDate != null) { whereObjs.add(maxDate); } + Integer offset = page*PROCESSES_PAGE_SIZE; + Integer size = PROCESSES_PAGE_SIZE; + + whereObjs.add(offset); + whereObjs.add(size); + String pattern = PENDING + "|" + DISABLED + "|" + FAILED_TO_START + "|" + FAILED_TO_SCHEDULE + "|" + CLAIMED_BY_WORKER + "|" + RUNNING + "|" + COMPLETE + "|" + RESOLVED + "|" + FAIL + "|" + INCIDENT; @@ -928,15 +934,16 @@ public List> getFilteredProcessInstances( String cwsQuery = "SELECT * " + - "FROM cws_sched_worker_proc_inst " + - "WHERE " + - (procInstId != null ? "proc_inst_id=? AND " : "") + - (procDefKey != null ? "proc_def_key=? AND " : "") + - (minDate != null ? "created_time >= ? AND " : "") + - (maxDate != null ? "created_time <= ? AND " : "") + - (statusList != null ? "status IN "+statusClause+" AND " : "") + - " proc_inst_id IS NULL " + // don't get any started processes - "ORDER BY created_time " + dateOrderBy + " "; + "FROM cws_sched_worker_proc_inst " + + "WHERE " + + (procInstId != null ? "proc_inst_id=? AND " : "") + + (procDefKey != null ? "proc_def_key=? AND " : "") + + (minDate != null ? "created_time >= ? AND " : "") + + (maxDate != null ? "created_time <= ? AND " : "") + + (statusList != null ? "status IN "+statusClause+" AND " : "") + + " proc_inst_id IS NULL " + // don't get any started processes + "ORDER BY created_time " + dateOrderBy + " " + + "LIMIT ?,?"; List> cwsRows = jdbcTemplate.queryForList(cwsQuery, whereObjs.toArray()); @@ -945,59 +952,79 @@ public List> getFilteredProcessInstances( String camundaQuery = "SELECT " + - // If there is no corresponding row in the CWS, table, then this wasn't scheduled - " CI.initiation_key AS initiation_key, " + - " CI.created_time AS created_time, " + - " CI.updated_time AS updated_time, " + - " CI.claimed_by_worker AS claimed_by_worker, " + - " CI.started_by_worker AS started_by_worker, " + - " PI.proc_inst_id AS proc_inst_id, " + - " PI.super_proc_inst_id AS super_proc_inst_id, " + - " PI.proc_def_key AS proc_def_key, " + - " PI.start_time AS proc_start_time, " + - " PI.end_time AS proc_end_time, " + - " PI.status AS status " + - "FROM cws_proc_inst_status PI " + - "LEFT JOIN cws_sched_worker_proc_inst CI " + - "ON PI.proc_inst_id = CI.proc_inst_id " + - "WHERE " + - (superProcInstId != null ? superProcInstId.equalsIgnoreCase("NULL") ? "PI.super_proc_inst_id IS NULL AND " : "PI.super_proc_inst_id=? AND " : "") + - (procInstId != null ? "PI.proc_inst_id=? AND " : "") + - (procDefKey != null ? "PI.proc_def_key=? AND " : "") + - (statusList != null ? "PI.status IN "+statusClause+" AND " : "") + - (minDate != null ? "PI.start_time >= ? AND " : "") + - (maxDate != null ? "PI.start_time <= ? AND " : "") + - " 1=1 " + - "ORDER BY PI.start_time " + dateOrderBy + " "; + // If there is no corresponding row in the CWS, table, then this wasn't scheduled + " CI.initiation_key AS initiation_key, " + + " CI.created_time AS created_time, " + + " CI.updated_time AS updated_time, " + + " CI.claimed_by_worker AS claimed_by_worker, " + + " CI.started_by_worker AS started_by_worker, " + + " PI.proc_inst_id AS proc_inst_id, " + + " PI.super_proc_inst_id AS super_proc_inst_id, " + + " PI.proc_def_key AS proc_def_key, " + + " PI.start_time AS proc_start_time, " + + " PI.end_time AS proc_end_time, " + + " PI.status AS status " + + "FROM cws_proc_inst_status PI " + + "LEFT JOIN cws_sched_worker_proc_inst CI " + + "ON PI.proc_inst_id = CI.proc_inst_id " + + "WHERE " + + (superProcInstId != null ? superProcInstId.equalsIgnoreCase("NULL") ? "PI.super_proc_inst_id IS NULL AND " : "PI.super_proc_inst_id=? AND " : "") + + (procInstId != null ? "PI.proc_inst_id=? AND " : "") + + (procDefKey != null ? "PI.proc_def_key=? AND " : "") + + (statusList != null ? "PI.status IN "+statusClause+" AND " : "") + + (minDate != null ? "PI.start_time >= ? AND " : "") + + (maxDate != null ? "PI.start_time <= ? AND " : "") + + " 1=1 " + + "ORDER BY PI.start_time " + dateOrderBy + " " + + "LIMIT ?,?"; List> camundaRows = jdbcTemplate.queryForList(camundaQuery, whereObjs.toArray()); - + // JOIN THE SETS... // // FINAL SET = CWS (PENDING, FAILED_TO_START) + Camunda (running, failed, completed) - // + // List> ret = new ArrayList>(); - + // Get the CWS rows, and add them in. // (these will only be the 'pending' rows // - for (Map cwsRow : cwsRows) { - Map finalRow = new HashMap(); - finalRow.put("uuid", (String)cwsRow.get("uuid")); - finalRow.put("proc_def_key", (String)cwsRow.get("proc_def_key")); - finalRow.put("proc_inst_id", (String)cwsRow.get("proc_inst_id")); - finalRow.put("super_proc_inst_id", null); // cws rows will never have a super proc inst id since it's started by the user - finalRow.put("status", (String)cwsRow.get("status")); - finalRow.put("initiation_key", (String)cwsRow.get("initiation_key")); - finalRow.put("created_time", (Timestamp)cwsRow.get("created_time")); - finalRow.put("updated_time", (Timestamp)cwsRow.get("updated_time")); - finalRow.put("claimed_by_worker", (String)cwsRow.get("claimed_by_worker")); - finalRow.put("started_by_worker", (String)cwsRow.get("started_by_worker")); - finalRow.put("proc_start_time", null); // pending rows haven't actually run yet - finalRow.put("proc_end_time", null); // pending rows haven't actually run yet - ret.add(finalRow); + if (superProcInstId == null) { + for (Map cwsRow : cwsRows) { + Map finalRow = new HashMap(); + finalRow.put("uuid", (String)cwsRow.get("uuid")); + finalRow.put("proc_def_key", (String)cwsRow.get("proc_def_key")); + finalRow.put("proc_inst_id", (String)cwsRow.get("proc_inst_id")); + finalRow.put("super_proc_inst_id", null); // cws rows will never have a super proc inst id since it's started by the user + finalRow.put("status", (String)cwsRow.get("status")); + finalRow.put("initiation_key", (String)cwsRow.get("initiation_key")); + finalRow.put("created_time", (Timestamp)cwsRow.get("created_time")); + finalRow.put("updated_time", (Timestamp)cwsRow.get("updated_time")); + finalRow.put("claimed_by_worker", (String)cwsRow.get("claimed_by_worker")); + finalRow.put("started_by_worker", (String)cwsRow.get("started_by_worker")); + finalRow.put("proc_start_time", null); // pending rows haven't actually run yet + finalRow.put("proc_end_time", null); // pending rows haven't actually run yet + ret.add(finalRow); + } + } else if (superProcInstId.equalsIgnoreCase("NULL")) { + for (Map cwsRow : cwsRows) { + Map finalRow = new HashMap(); + finalRow.put("uuid", (String)cwsRow.get("uuid")); + finalRow.put("proc_def_key", (String)cwsRow.get("proc_def_key")); + finalRow.put("proc_inst_id", (String)cwsRow.get("proc_inst_id")); + finalRow.put("super_proc_inst_id", null); // cws rows will never have a super proc inst id since it's started by the user + finalRow.put("status", (String)cwsRow.get("status")); + finalRow.put("initiation_key", (String)cwsRow.get("initiation_key")); + finalRow.put("created_time", (Timestamp)cwsRow.get("created_time")); + finalRow.put("updated_time", (Timestamp)cwsRow.get("updated_time")); + finalRow.put("claimed_by_worker", (String)cwsRow.get("claimed_by_worker")); + finalRow.put("started_by_worker", (String)cwsRow.get("started_by_worker")); + finalRow.put("proc_start_time", null); // pending rows haven't actually run yet + finalRow.put("proc_end_time", null); // pending rows haven't actually run yet + ret.add(finalRow); + } } - + // Get the Camunda rows, and add them in // for (Map camundaRow : camundaRows) { @@ -1018,7 +1045,7 @@ public List> getFilteredProcessInstances( finalRow.put("proc_end_time", (Timestamp)camundaRow.get("proc_end_time")); ret.add(finalRow); } - + return ret; } @@ -1040,21 +1067,21 @@ public List> getProcessInstanceStats(String lastNumHours) { String query = "SELECT " + - " proc_def_key, " + - " status, " + - " COUNT(*) AS cnt " + - "FROM cws_sched_worker_proc_inst " + - "WHERE status IN ('" + PENDING + "', '" + DISABLED + "', '" + FAILED_TO_START + "') " + - "AND (created_time > ? OR updated_time > ?) " + - "GROUP BY proc_def_key, status " + - "UNION ALL " + - "SELECT " + - " proc_def_key, " + - " status, " + - " COUNT(*) AS cnt " + - "FROM cws_proc_inst_status " + - "WHERE (start_time > ? OR end_time > ?) " + - "GROUP BY proc_def_key, status "; + " proc_def_key, " + + " status, " + + " COUNT(*) AS cnt " + + "FROM cws_sched_worker_proc_inst " + + "WHERE status IN ('" + PENDING + "', '" + DISABLED + "', '" + FAILED_TO_START + "') " + + "AND (created_time > ? OR updated_time > ?) " + + "GROUP BY proc_def_key, status " + + "UNION ALL " + + "SELECT " + + " proc_def_key, " + + " status, " + + " COUNT(*) AS cnt " + + "FROM cws_proc_inst_status " + + "WHERE (start_time > ? OR end_time > ?) " + + "GROUP BY proc_def_key, status "; List> camundaAndCwsStatuses = jdbcTemplate.queryForList(query, time, time, time, time); @@ -1074,30 +1101,30 @@ public List> getStatusByBusinessKey(String procDefKey, String String query = "SELECT " + - " status, " + - " COUNT(*) AS cnt " + - "FROM cws_sched_worker_proc_inst " + - "WHERE status IN ('" + PENDING + "', '" + DISABLED + "', '" + FAILED_TO_START + "') " + - "AND proc_business_key = ? " + - "AND proc_def_key = ? " + - "GROUP BY proc_def_key, status " + - "UNION ALL " + - "SELECT " + - " status, " + - " COUNT(*) AS cnt " + - "FROM cws_proc_inst_status " + - "WHERE business_key = ? " + - "AND proc_def_key = ? " + - "GROUP BY proc_def_key, status "; + " status, " + + " COUNT(*) AS cnt " + + "FROM cws_sched_worker_proc_inst " + + "WHERE status IN ('" + PENDING + "', '" + DISABLED + "', '" + FAILED_TO_START + "') " + + "AND proc_business_key = ? " + + "AND proc_def_key = ? " + + "GROUP BY proc_def_key, status " + + "UNION ALL " + + "SELECT " + + " status, " + + " COUNT(*) AS cnt " + + "FROM cws_proc_inst_status " + + "WHERE business_key = ? " + + "AND proc_def_key = ? " + + "GROUP BY proc_def_key, status "; List> camundaAndCwsStatuses = jdbcTemplate.queryForList(query, businessKey, procDefKey, businessKey, procDefKey); return new ArrayList<>(camundaAndCwsStatuses); } - + /** * Used by worker syncCounters method - * + * * List of [uuid, proc_def_key, status] */ public List> getStatsForScheduledProcs(Set cwsSchedUuids) { @@ -1111,12 +1138,12 @@ public List> getStatsForScheduledProcs(Set cwsSchedUu } uuidInClause += ")"; //log.debug(uuidInClause); - + List> camundaStatuses = jdbcTemplate.queryForList( - "SELECT DISTINCT " + + "SELECT DISTINCT " + " CI.uuid AS uuid, " + " PI.PROC_DEF_KEY_ AS proc_def_key, " + - PROC_INST_STATUS_SQL + " AS status " + + PROC_INST_STATUS_SQL + " AS status " + "FROM ACT_HI_PROCINST PI " + " LEFT JOIN cws_sched_worker_proc_inst CI " + " ON PI.PROC_INST_ID_ = CI.proc_inst_id " + @@ -1124,14 +1151,14 @@ public List> getStatsForScheduledProcs(Set cwsSchedUu " ON " + " PI.PROC_INST_ID_ = AI.PROC_INST_ID_ " + " AND " + - " (AI.END_TIME_ is null or AI.ACT_TYPE_ LIKE '%ndEvent' AND PI.END_TIME_ IS NOT NULL) " + + " (AI.END_TIME_ is null or AI.ACT_TYPE_ LIKE '%ndEvent' AND PI.END_TIME_ IS NOT NULL) " + "WHERE " + " (AI.PARENT_ACT_INST_ID_ IS NULL OR AI.PARENT_ACT_INST_ID_ NOT LIKE 'SubProcess%') " + " AND " + PROC_INST_STATUS_SQL + " IN ('complete','running', 'fail') " + " AND " + " CI.uuid in " + uuidInClause); - + List> cwsStatuses = jdbcTemplate.queryForList( "SELECT DISTINCT " + " uuid, " + @@ -1142,93 +1169,93 @@ public List> getStatsForScheduledProcs(Set cwsSchedUu " uuid in " + uuidInClause + " " + " AND " + " proc_inst_id IS NULL " // don't get any started processes (covered in above query) - ); - + ); + log.debug(camundaStatuses.size() + " camunda rows, " + cwsStatuses.size() + " cwsStatuses rows."); - + ret.addAll(camundaStatuses); ret.addAll(cwsStatuses); } - + return ret; } - - + + /** - * + * */ public List> getRunningProcessInstances() { return jdbcTemplate.queryForList( - "SELECT proc_inst_id, uuid, proc_def_key, status " + - "FROM cws_sched_worker_proc_inst " + - "WHERE status='running'"); + "SELECT proc_inst_id, uuid, proc_def_key, status " + + "FROM cws_sched_worker_proc_inst " + + "WHERE status='running'"); } - - + + /** - * + * */ public List> getIncompleteProcessInstancesForWorker(String workerId) { return jdbcTemplate.queryForList( - "SELECT proc_def_key, COUNT(*) as cnt " + - "FROM cws_sched_worker_proc_inst " + - "WHERE started_by_worker=? AND status IN ('pending', 'running') " + - "GROUP BY proc_def_key", - new Object[] {workerId}); - } - - + "SELECT proc_def_key, COUNT(*) as cnt " + + "FROM cws_sched_worker_proc_inst " + + "WHERE started_by_worker=? AND status IN ('pending', 'running') " + + "GROUP BY proc_def_key", + new Object[] {workerId}); + } + + /** - * + * */ public List> getPendingProcessInstances() { return jdbcTemplate.queryForList( - "SELECT * " + - "FROM cws_sched_worker_proc_inst " + - "WHERE status = '" + PENDING + "'"); + "SELECT * " + + "FROM cws_sched_worker_proc_inst " + + "WHERE status = '" + PENDING + "'"); } - - + + /** * Returns mapping of process instance statuses. * If procInstId is specified, then returns information about a single process instance, * otherwise information about all process instances is returned. - * + * * Camunda will first create a row in ACT_HI_PROCINST upon process start. * Then when the process completes, it will create multiple rows (one per activity) in ACT_HI_ACTINST. - * + * */ public List> getProcInstStatus(String procInstId, Integer limit) { String query = - "SELECT DISTINCT " + - " PI.PROC_DEF_ID_ AS procDefKey," + - " PI.PROC_INST_ID_ AS procInstId, " + - " PI.START_TIME_ AS startTime, " + - " PI.END_TIME_ AS endTime, " + - " PI.DURATION_ AS duration, " + - " PI.END_ACT_ID_ AS endActivityId, " + - PROC_INST_STATUS_SQL + " AS status " + - "FROM " + - " ACT_HI_PROCINST PI " + - " LEFT JOIN " + - " ACT_HI_ACTINST AI " + - " ON " + - " PI.PROC_INST_ID_ = AI.PROC_INST_ID_ AND " + - " ((PI.END_ACT_ID_ IS NULL) OR AI.ACT_ID_ = PI.END_ACT_ID_) " + - "WHERE " + - " PI.PROC_INST_ID_ = '"+procInstId+"' AND " + - // don't be fooled by sub-process end events. - // This clause was put in because of CWS-350 - " AI.PARENT_ACT_INST_ID_ NOT LIKE 'SubProcess%' " + - "ORDER BY PI.START_TIME_ desc " + - (limit == null ? "" : " LIMIT "+limit); + "SELECT DISTINCT " + + " PI.PROC_DEF_ID_ AS procDefKey," + + " PI.PROC_INST_ID_ AS procInstId, " + + " PI.START_TIME_ AS startTime, " + + " PI.END_TIME_ AS endTime, " + + " PI.DURATION_ AS duration, " + + " PI.END_ACT_ID_ AS endActivityId, " + + PROC_INST_STATUS_SQL + " AS status " + + "FROM " + + " ACT_HI_PROCINST PI " + + " LEFT JOIN " + + " ACT_HI_ACTINST AI " + + " ON " + + " PI.PROC_INST_ID_ = AI.PROC_INST_ID_ AND " + + " ((PI.END_ACT_ID_ IS NULL) OR AI.ACT_ID_ = PI.END_ACT_ID_) " + + "WHERE " + + " PI.PROC_INST_ID_ = '"+procInstId+"' AND " + + // don't be fooled by sub-process end events. + // This clause was put in because of CWS-350 + " AI.PARENT_ACT_INST_ID_ NOT LIKE 'SubProcess%' " + + "ORDER BY PI.START_TIME_ desc " + + (limit == null ? "" : " LIMIT "+limit); log.trace("QUERY: " + query); return jdbcTemplate.queryForList(query); } - - + + /** - * + * */ public void updateWorkerProcDefEnabled(String workerId, String procDefKey, String deploymentId, boolean isEnabled) throws Exception { int numUpdated = 0; @@ -1238,16 +1265,16 @@ public void updateWorkerProcDefEnabled(String workerId, String procDefKey, Strin // If the row already existed, then update the row. // numUpdated = jdbcTemplate.update( - "INSERT IGNORE INTO cws_worker_proc_def " + - "(worker_id, proc_def_key, max_instances, deployment_id, accepting_new) " + - "VALUES (?, ?, ?, ?, ?)", - new Object[] {workerId, procDefKey, DEFAULT_WORKER_PROC_DEF_MAX_INSTANCES, deploymentId, isEnabled}); + "INSERT IGNORE INTO cws_worker_proc_def " + + "(worker_id, proc_def_key, max_instances, deployment_id, accepting_new) " + + "VALUES (?, ?, ?, ?, ?)", + new Object[] {workerId, procDefKey, DEFAULT_WORKER_PROC_DEF_MAX_INSTANCES, deploymentId, isEnabled}); if (numUpdated == 0) { // row was already there, so just update it numUpdated = jdbcTemplate.update( - "UPDATE cws_worker_proc_def " + - "SET accepting_new=1 " + - "WHERE worker_id=? AND proc_def_key=?", - new Object[] {workerId, procDefKey}); + "UPDATE cws_worker_proc_def " + + "SET accepting_new=1 " + + "WHERE worker_id=? AND proc_def_key=?", + new Object[] {workerId, procDefKey}); log.debug("Updated (set accepting_new = 1) " + numUpdated + " row(s) in the cws_worker_proc_def table..."); } else { @@ -1259,204 +1286,204 @@ public void updateWorkerProcDefEnabled(String workerId, String procDefKey, Strin // then update the accepting_new flag // numUpdated = jdbcTemplate.update( - "UPDATE cws_worker_proc_def " + - "SET accepting_new=0 " + - "WHERE worker_id=? AND proc_def_key=?", - new Object[] {workerId, procDefKey}); + "UPDATE cws_worker_proc_def " + + "SET accepting_new=0 " + + "WHERE worker_id=? AND proc_def_key=?", + new Object[] {workerId, procDefKey}); log.debug("Updated (set accepting_new = 0) " + numUpdated + " row(s) in the cws_worker_proc_def table..."); } } - - + + /** - * + * */ public void updateWorkerProcDefLimit(String workerId, String procDefKey, int newLimit) throws Exception { int numUpdated = jdbcTemplate.update( - "UPDATE cws_worker_proc_def " + - "SET max_instances=? " + - "WHERE worker_id=? AND proc_def_key=?", - new Object[] {newLimit, workerId, procDefKey}); - + "UPDATE cws_worker_proc_def " + + "SET max_instances=? " + + "WHERE worker_id=? AND proc_def_key=?", + new Object[] {newLimit, workerId, procDefKey}); + log.debug("updated "+numUpdated + " rows"); } - - + + /** - * + * */ public void updateWorkerProcDefDeploymentId(String workerId, String procDefKey, String newDeploymentId) throws Exception { int numUpdated = jdbcTemplate.update( - "UPDATE cws_worker_proc_def " + - "SET deployment_id=? " + - "WHERE worker_id=? AND proc_def_key=?", - new Object[] {newDeploymentId, workerId, procDefKey}); - + "UPDATE cws_worker_proc_def " + + "SET deployment_id=? " + + "WHERE worker_id=? AND proc_def_key=?", + new Object[] {newDeploymentId, workerId, procDefKey}); + log.trace("updated "+numUpdated + " rows"); } - - + + /** - * + * */ public void updateWorkerNumJobExecutorThreads(String workerId, int numThreads) { int numUpdated = jdbcTemplate.update( - "UPDATE cws_worker " + - "SET job_executor_max_pool_size=? " + - "WHERE id=?", - new Object[] {numThreads, workerId}); + "UPDATE cws_worker " + + "SET job_executor_max_pool_size=? " + + "WHERE id=?", + new Object[] {numThreads, workerId}); log.debug("updated "+numUpdated + " rows"); } - - + + /** - * + * */ public void updateWorkerStatus(String workerId, String status) { int numRowsUpdated = jdbcTemplate.update( - "UPDATE cws_worker SET status=? WHERE id=?", - new Object[] { status, workerId } + "UPDATE cws_worker SET status=? WHERE id=?", + new Object[] { status, workerId } ); log.debug("setWorkerStatus ('" + workerId + "', " + status + "): Updated " + numRowsUpdated + " rows."); } - + public void setWorkerAcceptingNew(boolean acceptingNew, String workerId) { jdbcTemplate.update( - "UPDATE cws_worker_proc_def " + - "SET accepting_new=? " + - "WHERE worker_id=?", - new Object[] { acceptingNew, workerId } + "UPDATE cws_worker_proc_def " + + "SET accepting_new=? " + + "WHERE worker_id=?", + new Object[] { acceptingNew, workerId } ); } - - + + /** - * + * */ public List> getWorkerProcDefRows() { return jdbcTemplate.queryForList( - "SELECT * FROM cws_worker_proc_def"); + "SELECT * FROM cws_worker_proc_def"); } - - + + /** * Get proc defs where accepting_new is 1, and worker is alive (up) */ public List getAcceptableProcDefKeys() { return jdbcTemplate.queryForList( - "SELECT DISTINCT proc_def_key " + - "FROM cws_worker_proc_def WPD JOIN cws_worker W " + - "ON W.status='up' AND W.id=WPD.worker_id AND WPD.accepting_new = 1", - String.class + "SELECT DISTINCT proc_def_key " + + "FROM cws_worker_proc_def WPD JOIN cws_worker W " + + "ON W.status='up' AND W.id=WPD.worker_id AND WPD.accepting_new = 1", + String.class ); } - - + + /** - * - */ + * + */ public List> getWorkersForProcDefKey(String procDefKey){ return jdbcTemplate.queryForList( - "SELECT w.id, w.name, w.status, w.cws_install_type, pd.proc_def_key,pd.accepting_new "+ - "FROM cws_worker AS w "+ - "LEFT JOIN cws_worker_proc_def AS pd ON w.id=pd.worker_id AND "+ - "(pd.proc_def_key='" + procDefKey + "' OR pd.proc_def_key IS NULL) ORDER BY w.name"); + "SELECT w.id, w.name, w.status, w.cws_install_type, pd.proc_def_key,pd.accepting_new "+ + "FROM cws_worker AS w "+ + "LEFT JOIN cws_worker_proc_def AS pd ON w.id=pd.worker_id AND "+ + "(pd.proc_def_key='" + procDefKey + "' OR pd.proc_def_key IS NULL) ORDER BY w.name"); } - - + + /** - * - */ + * + */ public List> getProcDefWorkerCount(){ return jdbcTemplate.queryForList( - "SELECT prc.KEY_ AS pdk , IFNULL(SUM(pd.accepting_new),0) AS workers "+ - "FROM ACT_RE_PROCDEF AS prc "+ - "LEFT JOIN cws_worker_proc_def AS pd ON prc.KEY_=pd.proc_def_key GROUP BY KEY_"); - + "SELECT prc.KEY_ AS pdk , IFNULL(SUM(pd.accepting_new),0) AS workers "+ + "FROM ACT_RE_PROCDEF AS prc "+ + "LEFT JOIN cws_worker_proc_def AS pd ON prc.KEY_=pd.proc_def_key GROUP BY KEY_"); + // Simpler? : // select proc_def_key as pdk, sum(accepting_new) as workers from cws_worker_proc_def group by proc_def_key; } - - + + /** - * + * */ public Integer isWorkerProcDefAcceptingNew(String workerId, String deploymentId) { try { return jdbcTemplate.queryForObject( - "SELECT accepting_new " + - "FROM cws_worker_proc_def " + - "WHERE worker_id=? AND deployment_id=?", - new Object[]{workerId, deploymentId}, Integer.class); + "SELECT accepting_new " + + "FROM cws_worker_proc_def " + + "WHERE worker_id=? AND deployment_id=?", + new Object[]{workerId, deploymentId}, Integer.class); } catch (EmptyResultDataAccessException e) { log.debug("no cws_worker_proc_def row found for workerId = '" + workerId + "', deploymentId = '" + deploymentId + "'"); return null; } } - - + + /** - * + * */ public List> getWorkerProcDefRows(String workerId, Boolean acceptingNew) { if (acceptingNew != null) { return jdbcTemplate.queryForList( - "SELECT * FROM cws_worker_proc_def " + - "WHERE worker_id=? AND accepting_new=?", - new Object[]{workerId, acceptingNew}); + "SELECT * FROM cws_worker_proc_def " + + "WHERE worker_id=? AND accepting_new=?", + new Object[]{workerId, acceptingNew}); } else { return jdbcTemplate.queryForList( - "SELECT * FROM cws_worker_proc_def " + - "WHERE worker_id=?", - new Object[]{workerId}); + "SELECT * FROM cws_worker_proc_def " + + "WHERE worker_id=?", + new Object[]{workerId}); } } - - + + /** - * - */ + * + */ public void insertCwsToken(String cwsToken, String username, Timestamp expirationTime) { jdbcTemplate.update( - "INSERT IGNORE INTO cws_token " + - "(token, username, expiration_time) " + - "VALUES (?, ?, ?)", - new Object[] {cwsToken, username, expirationTime}); + "INSERT IGNORE INTO cws_token " + + "(token, username, expiration_time) " + + "VALUES (?, ?, ?)", + new Object[] {cwsToken, username, expirationTime}); } - - + + /** - * - */ + * + */ public void deleteCwsToken(String cwsToken, String username) { jdbcTemplate.update( - "DELETE FROM cws_token " + - "where token=? and username=?", - new Object[] {cwsToken, username}); + "DELETE FROM cws_token " + + "where token=? and username=?", + new Object[] {cwsToken, username}); } - - + + /** - * - */ + * + */ public Map getCwsToken(String cwsToken, String username) { return jdbcTemplate.queryForMap( - "SELECT * FROM cws_token " + - "WHERE token=? AND username=?", - new Object[] { cwsToken, username } + "SELECT * FROM cws_token " + + "WHERE token=? AND username=?", + new Object[] { cwsToken, username } ); } - - + + /** - * - */ + * + */ public List> getAllCwsTokens() { return jdbcTemplate.queryForList( - "SELECT * FROM cws_token" + "SELECT * FROM cws_token" ); } @@ -1468,9 +1495,9 @@ public List> getAllCwsTokens() { */ private int updateStatus(String status, String procInstId) { String query = - "UPDATE cws_proc_inst_status " + - "SET status = ? " + - "WHERE proc_inst_id = ? "; + "UPDATE cws_proc_inst_status " + + "SET status = ? " + + "WHERE proc_inst_id = ? "; return jdbcTemplate.update(query, status, procInstId); } @@ -1487,9 +1514,9 @@ private int updateStatusBulk(String status, List procInstIds) { String uuidSqlArray = buildSanitizedSqlArray(procInstIds, uuidPattern); String query = - "UPDATE cws_proc_inst_status " + - "SET status = ? " + - "WHERE proc_inst_id IN " + uuidSqlArray; + "UPDATE cws_proc_inst_status " + + "SET status = ? " + + "WHERE proc_inst_id IN " + uuidSqlArray; return jdbcTemplate.update(query, status); } @@ -1523,9 +1550,9 @@ public int retryFailedToStart(List uuids) { String uuidSqlArray = buildSanitizedSqlArray(uuids, uuidPattern); String query = - "UPDATE cws_sched_worker_proc_inst " + - "SET status = 'pending' " + - "WHERE uuid IN " + uuidSqlArray; + "UPDATE cws_sched_worker_proc_inst " + + "SET status = 'pending' " + + "WHERE uuid IN " + uuidSqlArray; return jdbcTemplate.update(query); } diff --git a/cws-service/src/main/java/jpl/cws/controller/RestService.java b/cws-service/src/main/java/jpl/cws/controller/RestService.java index 12cd6d70..8dcbc894 100644 --- a/cws-service/src/main/java/jpl/cws/controller/RestService.java +++ b/cws-service/src/main/java/jpl/cws/controller/RestService.java @@ -1103,8 +1103,12 @@ public GsonUTCDateAdapter() { @RequestParam(value = "procDefKey", required=false) String procDefKey, @RequestParam(value = "status", required=false) String status, @RequestParam(value = "minDate", required=false) String minDate, - @RequestParam(value = "maxDate", required=false) String maxDate + @RequestParam(value = "maxDate", required=false) String maxDate, + @RequestParam(value = "maxReturn", required=false, defaultValue="5000") String maxReturn ) { + + Integer intMaxReturn = Integer.parseInt(maxReturn); + log.debug("REST: getProcessInstancesSize (superProcInstId='" + superProcInstId + "', procInstId='" + procInstId + "', procDefKey='"+procDefKey+ @@ -1114,6 +1118,9 @@ public GsonUTCDateAdapter() { try { size = dbService.getFilteredProcessInstancesSize( superProcInstId, procInstId, procDefKey, status, minDate, maxDate); + if (intMaxReturn > 0 && intMaxReturn < size) { + size = intMaxReturn; + } } catch (Exception e) { log.error("Problem while getFilteredProcessInstancesSize", e); @@ -1126,14 +1133,13 @@ public GsonUTCDateAdapter() { @PathVariable String procInstId) { List instances = null; instances = cwsConsoleService.getFilteredProcessInstancesCamunda( - null, procInstId, null, null, null, null, "DESC", 1); + null, procInstId, null, null, null, null, "DESC", 0); if (instances.size() == 0) { return null; } else { return instances.get(0).getStatus(); } } - /** * REST method used to get Processes table JSON @@ -1148,14 +1154,16 @@ public GsonUTCDateAdapter() { @RequestParam(value = "minDate", required=false) String minDate, @RequestParam(value = "maxDate", required=false) String maxDate, @RequestParam(value = "dateOrderBy", required=false, defaultValue="DESC") String dateOrderBy, - @RequestParam(value = "page", required=false, defaultValue="0") String page + @RequestParam(value = "page", required=false, defaultValue="0") String page, + @RequestParam(value = "maxReturn", required=false, defaultValue="5000") String maxReturn ) { List instances = null; - int recordsToReturn = 0; - Integer pageNum = Integer.parseInt(page); try { + Integer pageNum = Integer.parseInt(page); + Integer intMaxReturn = Integer.parseInt(maxReturn); + dateOrderBy = dateOrderBy.toUpperCase(); if (!dateOrderBy.equals("DESC") && !dateOrderBy.equals("ASC")) { log.error("Invalid dateOrderBy of " + dateOrderBy + "! Forcing to be 'DESC'"); @@ -1166,22 +1174,22 @@ public GsonUTCDateAdapter() { "', procInstId='" + procInstId + "', procDefKey='"+procDefKey+ "', status='"+status+"', minDate="+minDate+", maxDate="+maxDate+ - ", dateOrderBy="+dateOrderBy+", page="+page+")"); + ", dateOrderBy="+dateOrderBy+")"); instances = cwsConsoleService.getFilteredProcessInstancesCamunda( superProcInstId, procInstId, procDefKey, status, minDate, maxDate, dateOrderBy, pageNum); - if (pageNum > instances.size()) { - recordsToReturn = instances.size(); - } else { - recordsToReturn = pageNum; + + if ((intMaxReturn != -1) && (instances.size() > intMaxReturn)) { + instances = instances.subList(0, intMaxReturn); } + } catch (Exception e) { log.error("Problem getting process instance information!", e); // return an empty set return new GsonBuilder().setPrettyPrinting().create().toJson(new ArrayList()); } - return new GsonBuilder().setPrettyPrinting().create().toJson(instances.subList(0,recordsToReturn)); + return new GsonBuilder().serializeNulls().create().toJson(instances); } diff --git a/cws-service/src/main/java/jpl/cws/scheduler/CwsProcessInstance.java b/cws-service/src/main/java/jpl/cws/scheduler/CwsProcessInstance.java index 16e18938..4596a263 100644 --- a/cws-service/src/main/java/jpl/cws/scheduler/CwsProcessInstance.java +++ b/cws-service/src/main/java/jpl/cws/scheduler/CwsProcessInstance.java @@ -1,6 +1,7 @@ package jpl.cws.scheduler; import java.sql.Timestamp; +import java.util.Map; public class CwsProcessInstance { @@ -22,7 +23,7 @@ public class CwsProcessInstance { private Timestamp procStartTime; // from Camunda ACT_HI_PROCINST_ table private Timestamp procEndTime; // from Camunda ACT_HI_PROCINST_ table - private String inputVariables; + private Map inputVariables; public CwsProcessInstance( String uuid, @@ -37,7 +38,7 @@ public CwsProcessInstance( String startedByWorker, Timestamp procStartTime, Timestamp procEndTime, - String inputVariables) { + Map inputVariables) { super(); this.uuid = uuid; this.procDefKey = procDefKey; @@ -86,11 +87,11 @@ public String getStatus() { public void setStatus(String status) { this.status = status; } - public void setInputVariables(String input) { + public void setInputVariables(Map input) { this.inputVariables = input; } - public String getInputVariables() { + public Map getInputVariables() { return inputVariables; } diff --git a/cws-service/src/main/java/jpl/cws/service/CwsConsoleService.java b/cws-service/src/main/java/jpl/cws/service/CwsConsoleService.java index bef32a67..0ab1d8dd 100644 --- a/cws-service/src/main/java/jpl/cws/service/CwsConsoleService.java +++ b/cws-service/src/main/java/jpl/cws/service/CwsConsoleService.java @@ -612,50 +612,31 @@ public LogHistory getHistoryForProcess(String processInstanceId) { return history; } - public String getInputVariablesForProcess(String processInstanceId) { + public Map getInputVariablesForProcess(String processInstanceId) { + Map inputVarMap = new HashMap(); List historyDetails = new ArrayList(); getHistoryVarDetails(historyDetails, processInstanceId); - - String output = ""; - String before = ""; - String after = ""; - int putAllAfter = 0; - int count = 0; + + if (processInstanceId == null) { + return inputVarMap; + } for (HistoryDetail historyDetail : historyDetails) { if (historyDetail.type.equals("VarUpdate") && historyDetail.activity.equals(processInstanceId)) { - if (count > 3) { - putAllAfter = 1; - } String message = historyDetail.message; String varType = message.substring(message.indexOf("("), message.indexOf(")")+1); String varName = message.substring(message.indexOf(")")+2); varName = varName.substring(0, varName.indexOf("=")-1) + " " + varType; String varValue = message.substring(message.indexOf("=")+2); - String temp = "
" + varName + ": " + varValue + "
" - + "

"; - if (varName.contains("workerId")) { - after = after + temp; - } else if (varName.contains("startedOnWorkerId")) { - after = after + temp; - putAllAfter = 1; - } else if (putAllAfter == 0) { - before = before + temp; + + if (inputVarMap.containsKey(varName)) { } else { - after = after + temp; + inputVarMap.put(varName, varValue); } - count++; + } } - if (after.isEmpty()) { - output = before; - } else { - output = before + "
Show All" + after + "
"; - } - return output; + return inputVarMap; } public List getExternalWorkersUiDTO() { @@ -1081,9 +1062,11 @@ public List getFilteredProcessInstancesCamunda(String superP String startedByWorker = (String) row.get("started_by_worker"); Timestamp procStartTime = (Timestamp) row.get("proc_start_time"); Timestamp procEndTime = (Timestamp) row.get("proc_end_time"); - String inputVars = ""; + Map inputVars; if (procInstIdObj != null) { - inputVars = getInputVariablesForProcess(procInstIdObj.toString()); + inputVars = getInputVariablesForProcess(procInstIdObj.toString()); + } else { + inputVars = new HashMap(); } CwsProcessInstance instance = new CwsProcessInstance(uuidObj == null ? null : uuidObj.toString(), procDefKeyObj == null ? null : procDefKeyObj.toString(), diff --git a/cws-test/src/test/java/jpl/cws/test/integration/ui/HistoryTestIT.java b/cws-test/src/test/java/jpl/cws/test/integration/ui/HistoryTestIT.java index 4d322fbd..e02107b4 100644 --- a/cws-test/src/test/java/jpl/cws/test/integration/ui/HistoryTestIT.java +++ b/cws-test/src/test/java/jpl/cws/test/integration/ui/HistoryTestIT.java @@ -63,9 +63,10 @@ public void runResultsTest() throws IOException { goToPage("processes"); - log.info("Filtering Test History Page results."); waitForElementXPath("//div[@id=\'processes-table_filter\']/label/input"); + sleep(5000); + driver.findElement(By.xpath("//div[@id=\'processes-table_filter\']/label/input")).click(); driver.findElement(By.xpath("//div[@id=\'processes-table_filter\']/label/input")).sendKeys("test_history_page"); driver.findElement(By.xpath("//div[@id=\'processes-table_filter\']/label/input")).sendKeys(Keys.ENTER); diff --git a/cws-test/src/test/java/jpl/cws/test/integration/ui/InitiatorsTestIT.java b/cws-test/src/test/java/jpl/cws/test/integration/ui/InitiatorsTestIT.java index d865c227..65400f77 100644 --- a/cws-test/src/test/java/jpl/cws/test/integration/ui/InitiatorsTestIT.java +++ b/cws-test/src/test/java/jpl/cws/test/integration/ui/InitiatorsTestIT.java @@ -4,6 +4,9 @@ import java.io.IOException; import java.time.Duration; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; import org.junit.Test; import org.openqa.selenium.By; @@ -15,6 +18,9 @@ import org.openqa.selenium.support.ui.WebDriverWait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.logging.LogEntry; +import org.openqa.selenium.logging.LogType; import jpl.cws.test.WebTestUtil; @@ -191,6 +197,16 @@ public void runVariableProcTest() throws IOException { log.info("Filtering results for Test Initiators Page test."); waitForElementXPath("//div[@id=\'processes-table_filter\']/label/input"); + Set logtyp = driver.manage().logs().getAvailableLogTypes(); + for (String s : logtyp) { + log.info("BROWSER: " + logtyp); + } + LogEntries logEntries = driver.manage().logs().get(LogType.BROWSER); + List lg = logEntries.getAll(); + for(LogEntry logEntry : lg) { + log.info("BROWSER: " + logEntry); + } + driver.findElement(By.xpath("//div[@id=\'processes-table_filter\']/label/input")).click(); driver.findElement(By.xpath("//div[@id=\'processes-table_filter\']/label/input")).sendKeys("test_initiators_page"); driver.findElement(By.xpath("//div[@id=\'processes-table_filter\']/label/input")).sendKeys(Keys.ENTER); diff --git a/cws-ui/src/main/webapp/css/dashboard.css b/cws-ui/src/main/webapp/css/dashboard.css index 9ca0ab03..d3828a34 100644 --- a/cws-ui/src/main/webapp/css/dashboard.css +++ b/cws-ui/src/main/webapp/css/dashboard.css @@ -299,16 +299,6 @@ div[class*="bar-"]{ --button-diameter: 25px; --button-outline-width: 1px; --button-outline-color: rgb(141, 141, 141); - /* tooltip */ - --tooltip-bg: #f4f3f3; - --toolptip-border-radius: 4px; - --tooltip-font-family: Menlo, Roboto Mono, monospace; - --tooltip-font-size: 12px; - --tootip-text-color: rgb(50, 50, 50); - --tooltip-padding-x: 7px; - --tooltip-padding-y: 7px; - --tooltip-offset: 8px; - --tooltip-transition-duration: 0.3s; } .copy { @@ -325,57 +315,9 @@ div[class*="bar-"]{ float: right; } -.tooltip { - position: absolute; - opacity: 0; - visibility: 0; - top: 0; - left: 50%; - transform: translateX(-50%); - white-space: nowrap; - color: var(--tootip-text-color); - background: var(--tooltip-bg); - padding: var(--tooltip-padding-y) var(--tooltip-padding-x); - border-radius: var(--toolptip-border-radius); - pointer-events: none; - transition: all var(--tooltip-transition-duration) cubic-bezier(0.68, -0.55, 0.265, 1.55); -} - -.tooltip::before { - content: attr(data-text-initial); -} - -.tooltip::after { - content: ""; - position: absolute; - bottom: calc(var(--tooltip-padding-y) / 2 * -1); - width: var(--tooltip-padding-y); - height: var(--tooltip-padding-y); - background: inherit; - left: 50%; - transform: translateX(-50%) rotate(45deg); - z-index: -999; - pointer-events: none; -} - .copy svg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } - - -/* actions */ - -.copy:hover .tooltip, -.copy:focus:not(:focus-visible) .tooltip { - opacity: 1; - visibility: visible; - top: calc((100% + var(--tooltip-offset)) * -1); -} - -.copy:focus:not(:focus-visible) .tooltip::before { - content: attr(data-text-end); -} - diff --git a/cws-ui/src/main/webapp/css/microtip.css b/cws-ui/src/main/webapp/css/microtip.css new file mode 100644 index 00000000..eb90685f --- /dev/null +++ b/cws-ui/src/main/webapp/css/microtip.css @@ -0,0 +1,266 @@ +/* ------------------------------------------------------------------- + Microtip + + Modern, lightweight css-only tooltips + Just 1kb minified and gzipped + + @author Ghosh + @package Microtip + +---------------------------------------------------------------------- + 1. Base Styles + 2. Direction Modifiers + 3. Position Modifiers +--------------------------------------------------------------------*/ + + +/* ------------------------------------------------ + [1] Base Styles +-------------------------------------------------*/ + +[aria-label][role~="tooltip"] { + position: relative; +} + +[aria-label][role~="tooltip"]::before, +[aria-label][role~="tooltip"]::after { + transform: translate3d(0, 0, 0); + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + will-change: transform; + opacity: 0; + pointer-events: none; + transition: all var(--microtip-transition-duration, .18s) var(--microtip-transition-easing, ease-in-out) var(--microtip-transition-delay, 0s); + position: absolute; + box-sizing: border-box; + z-index: 10; + transform-origin: top; +} + +[aria-label][role~="tooltip"]::before { + background-size: 100% auto !important; + content: ""; +} + +[aria-label][role~="tooltip"]::after { + background: rgba(17, 17, 17, .9); + border-radius: 4px; + color: #ffffff; + content: attr(aria-label); + font-size: var(--microtip-font-size, 13px); + font-weight: var(--microtip-font-weight, normal); + text-transform: var(--microtip-text-transform, none); + padding: .5em 1em; + white-space: nowrap; + box-sizing: content-box; +} + +[aria-label][role~="tooltip"]:hover::before, +[aria-label][role~="tooltip"]:hover::after, +[aria-label][role~="tooltip"]:focus::before, +[aria-label][role~="tooltip"]:focus::after { + opacity: 1; + pointer-events: auto; +} + + + +/* ------------------------------------------------ + [2] Position Modifiers +-------------------------------------------------*/ + +[role~="tooltip"][data-microtip-position|="top"]::before { + background: url("../images/tooltip-top.svg"); + height: 6px; + width: 18px; + margin-bottom: 5px; +} + +[role~="tooltip"][data-microtip-position|="top"]::after { + margin-bottom: 11px; +} + +[role~="tooltip"][data-microtip-position|="top"]::before { + transform: translate3d(-50%, 0, 0); + bottom: 100%; + left: 50%; +} + +[role~="tooltip"][data-microtip-position|="top"]:hover::before { + transform: translate3d(-50%, -5px, 0); +} + +[role~="tooltip"][data-microtip-position|="top"]::after { + transform: translate3d(-50%, 0, 0); + bottom: 100%; + left: 50%; +} + +[role~="tooltip"][data-microtip-position="top"]:hover::after { + transform: translate3d(-50%, -5px, 0); +} + +/* ------------------------------------------------ + [2.1] Top Left +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position="top-left"]::after { + transform: translate3d(calc(-100% + 16px), 0, 0); + bottom: 100%; +} + +[role~="tooltip"][data-microtip-position="top-left"]:hover::after { + transform: translate3d(calc(-100% + 16px), -5px, 0); +} + + +/* ------------------------------------------------ + [2.2] Top Right +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position="top-right"]::after { + transform: translate3d(calc(0% + -16px), 0, 0); + bottom: 100%; +} + +[role~="tooltip"][data-microtip-position="top-right"]:hover::after { + transform: translate3d(calc(0% + -16px), -5px, 0); +} + + +/* ------------------------------------------------ + [2.3] Bottom +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position|="bottom"]::before { + background: url("../images/tooltip-bottom.svg"); + height: 6px; + width: 18px; + margin-top: 5px; + margin-bottom: 0; +} + +[role~="tooltip"][data-microtip-position|="bottom"]::after { + margin-top: 11px; +} + +[role~="tooltip"][data-microtip-position|="bottom"]::before { + transform: translate3d(-50%, -10px, 0); + bottom: auto; + left: 50%; + top: 100%; +} + +[role~="tooltip"][data-microtip-position|="bottom"]:hover::before { + transform: translate3d(-50%, 0, 0); +} + +[role~="tooltip"][data-microtip-position|="bottom"]::after { + transform: translate3d(-50%, -10px, 0); + top: 100%; + left: 50%; +} + +[role~="tooltip"][data-microtip-position="bottom"]:hover::after { + transform: translate3d(-50%, 0, 0); +} + + +/* ------------------------------------------------ + [2.4] Bottom Left +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position="bottom-left"]::after { + transform: translate3d(calc(-100% + 16px), -10px, 0); + top: 100%; +} + +[role~="tooltip"][data-microtip-position="bottom-left"]:hover::after { + transform: translate3d(calc(-100% + 16px), 0, 0); +} + + +/* ------------------------------------------------ + [2.5] Bottom Right +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position="bottom-right"]::after { + transform: translate3d(calc(0% + -16px), -10px, 0); + top: 100%; +} + +[role~="tooltip"][data-microtip-position="bottom-right"]:hover::after { + transform: translate3d(calc(0% + -16px), 0, 0); +} + + +/* ------------------------------------------------ + [2.6] Left +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position="left"]::before, +[role~="tooltip"][data-microtip-position="left"]::after { + bottom: auto; + left: auto; + right: 100%; + top: 50%; + transform: translate3d(10px, -50%, 0); +} + +[role~="tooltip"][data-microtip-position="left"]::before { + background: url("../images/tooltip-left.svg"); + height: 18px; + width: 6px; + margin-right: 5px; + margin-bottom: 0; +} + +[role~="tooltip"][data-microtip-position="left"]::after { + margin-right: 11px; +} + +[role~="tooltip"][data-microtip-position="left"]:hover::before, +[role~="tooltip"][data-microtip-position="left"]:hover::after { + transform: translate3d(0, -50%, 0); +} + + +/* ------------------------------------------------ + [2.7] Right +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-position="right"]::before, +[role~="tooltip"][data-microtip-position="right"]::after { + bottom: auto; + left: 100%; + top: 50%; + transform: translate3d(-10px, -50%, 0); +} + +[role~="tooltip"][data-microtip-position="right"]::before { + background: url("../images/tooltip-right.svg"); + height: 18px; + width: 6px; + margin-bottom: 0; + margin-left: 5px; +} + +[role~="tooltip"][data-microtip-position="right"]::after { + margin-left: 11px; +} + +[role~="tooltip"][data-microtip-position="right"]:hover::before, +[role~="tooltip"][data-microtip-position="right"]:hover::after { + transform: translate3d(0, -50%, 0); +} + +/* ------------------------------------------------ + [3] Size +-------------------------------------------------*/ +[role~="tooltip"][data-microtip-size="small"]::after { + white-space: initial; + width: 80px; +} + +[role~="tooltip"][data-microtip-size="medium"]::after { + white-space: initial; + width: 150px; +} + +[role~="tooltip"][data-microtip-size="large"]::after { + white-space: initial; + width: 260px; +} diff --git a/cws-ui/src/main/webapp/images/tooltip-bottom.svg b/cws-ui/src/main/webapp/images/tooltip-bottom.svg new file mode 100644 index 00000000..a8431526 --- /dev/null +++ b/cws-ui/src/main/webapp/images/tooltip-bottom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cws-ui/src/main/webapp/images/tooltip-left.svg b/cws-ui/src/main/webapp/images/tooltip-left.svg new file mode 100644 index 00000000..ce070d5a --- /dev/null +++ b/cws-ui/src/main/webapp/images/tooltip-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cws-ui/src/main/webapp/images/tooltip-right.svg b/cws-ui/src/main/webapp/images/tooltip-right.svg new file mode 100644 index 00000000..f3e741cc --- /dev/null +++ b/cws-ui/src/main/webapp/images/tooltip-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cws-ui/src/main/webapp/images/tooltip-top.svg b/cws-ui/src/main/webapp/images/tooltip-top.svg new file mode 100644 index 00000000..e25e328d --- /dev/null +++ b/cws-ui/src/main/webapp/images/tooltip-top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cws-ui/src/main/webapp/js/cws.js b/cws-ui/src/main/webapp/js/cws.js index de591b7d..96289bdb 100644 --- a/cws-ui/src/main/webapp/js/cws.js +++ b/cws-ui/src/main/webapp/js/cws.js @@ -22,3 +22,449 @@ function getQueryString(){ return params; } +function getInstanceCSV(procInstId) { + var outputCSV = ""; + var logLines = []; + var scrollId = ""; + var proc_info = {}; + var baseEsReq = { + "from": 0, + "size": 20, + "query": { + "bool": { + "must" :[] + } + }, + "sort": { "@timestamp": { "order": "asc" } } + }; + baseEsReq.query.bool.must.push({"query_string":{"fields":["procInstId"],"query" : "\"" + decodeURIComponent(procInstId) + "\""}}); + + //get process history + $.ajax({ + type: "GET", + url: "/${base}/rest/history/" + procInstId, + Accept : "application/json", + contentType: "application/json", + dataType: "json", + async: false + }).success(function(data) { + var status = data.state; + if (data.state === "COMPLETED") { + status = "Complete"; + } + else if (data.state === "ACTIVE") { + status = "Running"; + } + proc_info["process_definition"] = data.procDefKey; + proc_info["process_instance"] = data.procInstId; + proc_info["start_time"] = data.startTime; + proc_info["end_time"] = data.endTime; + proc_info["duration"] = convertMillis(data.duration); + proc_info["status"] = status; + for (const entry of data.details) { + let date = entry["date"]; + if (entry["message"].startsWith("Ended ")) { + date += " "; + } + const row = [date, entry["type"], entry["activity"], outputMessage(entry["message"])]; + logLines.push(row); + } + }).fail(function(xhr, err) { + console.error("Error getting instance JSON: " + xhr.responseText); + }); + + $.ajax({ + type: "GET", + url: "/${base}/rest/logs/get?source=" + encodeURIComponent(JSON.stringify(baseEsReq)), + Accept : "application/json", + contentType: "application/json", + dataType: "json", + async: false + }).success(function(data) { + var finished = false; + scrollId = data._scroll_id; + if (data.hits) { + for (const hit of data.hits.hits) { + const source = hit._source; + const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; + logLines.push(row); + + } + } + while (!finished) { + $.ajax({ + type: "POST", + url: "/${base}/rest/logs/get/scroll", + data: "scrollId=" + scrollId, + async: false, + success: function(data) { + if (data.hits) { + + if (data.hits.hits.length > 0) { + for (const hit of data.hits.hits) { + const source = hit._source; + const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; + logLines.push(row); + } + scrollId = data._scroll_id; + } + else { + finished = true; + } + } + }, + error: function(e) { + alert("Error retrieving history data."); + } + }); + } + }).fail(function(xhr, err) { + console.error("Error getting instance JSON: " + xhr.responseText); + }); + logLines.sort(function(a, b) { + var aTemp = a[0]; + //if there is a space in the last char, remove it + if (aTemp.charAt(aTemp.length - 1) == " ") { + aTemp = aTemp.substring(0, aTemp.length - 1); + } + var bTemp = b[0]; + //if there is a space in the last char, remove it + if (bTemp.charAt(bTemp.length - 1) == " ") { + bTemp = bTemp.substring(0, bTemp.length - 1); + } + var aDate = moment(aTemp); + var bDate = moment(bTemp); + if (aDate.isBefore(bDate)) return -1; + if (bDate.isBefore(aDate)) return 1; + return 0; + }); + + logLines.forEach(function(row) { + var data = row; + var details = data[3]; + var tmpDetails = ""; + var lineString = ""; + if (data[3].indexOf("Setting (json)") === -1) { + details = details.replaceAll('
', "\n"); + details = details.replaceAll("

", ""); + details = details.replaceAll("

", ""); + details = details.replaceAll('"' , '""'); + details = details.replaceAll('\n' , ' '); + //add first and last char as double quotes + details = '"' + details + '"'; + lineString = proc_info["process_definition"] + "," + proc_info["process_instance"] + "," + data[0] + "," + data[1] + "," + data[2] + "," + details + "\r\n"; + } else { + lineString = proc_info["process_definition"] + "," + proc_info["process_instance"] + "," + data[0] + "," + data[1] + "," + data[2] + ","; + //remove last char + if (data[3].indexOf("_in =") !== -1) { + lineString += '"' + details.substring(0, details.indexOf("_in =")+3) + " "; + details = details.substring(details.indexOf("_in =")+3); + } else { + lineString += '"' + details.substring(0, details.indexOf("_out =")+4) + " "; + details = details.substring(details.indexOf("_out =")+4); + } + //now we need to go through and get details from json string + //note: key is always after and value is the following td + while (details.indexOf("")+1); + var key = details.substring(0, details.indexOf("")); + details = details.substring(details.indexOf("")+4); + var value = details.substring(0, details.indexOf("")); + tmpDetails += key + ": " + value + "; "; + } + //check/clean tmpDetails + if (tmpDetails !== "") { + //replace all break points with new line + tmpDetails = tmpDetails.replaceAll(/
/g, " "); + //find and remove everything between and + tmpDetails = tmpDetails.replace(/.*<\/summary>/g, ""); + //find and remove
and
+ tmpDetails = tmpDetails.replace(/
/g, ""); + tmpDetails = tmpDetails.replace(/<\/details>/g, ""); + //CSV quirk: replace all " with "" + tmpDetails = tmpDetails.replaceAll('"' , '""'); + } + //remove last char + tmpDetails = tmpDetails.substring(0, tmpDetails.length-1); + tmpDetails = tmpDetails + '"'; + lineString += tmpDetails + "\r\n"; + } + lineString = lineString.replaceAll("", ""); + outputCSV = outputCSV + lineString; + } ); + return outputCSV; +}; + +function getInstanceJSON(procInstId) { + var outputJSON = {}; + var logLinesJSON = {}; + var logLines = []; + var scrollId = ""; + var baseEsReq = { + "from": 0, + "size": 20, + "query": { + "bool": { + "must" :[] + } + }, + "sort": { "@timestamp": { "order": "asc" } } + }; + baseEsReq.query.bool.must.push({"query_string":{"fields":["procInstId"],"query" : "\"" + decodeURIComponent(procInstId) + "\""}}); + + //get process history + $.ajax({ + type: "GET", + url: "/${base}/rest/history/" + procInstId, + Accept : "application/json", + contentType: "application/json", + dataType: "json", + async: false + }).success(function(data) { + var status = data.state; + if (data.state === "COMPLETED") { + status = "Complete"; + } + else if (data.state === "ACTIVE") { + status = "Running"; + } + var proc_info = { + "process_definition": data.procDefKey, + "process_instance": data.procInstId, + "start_time": data.startTime, + "end_time": data.endTime, + "duration": convertMillis(data.duration), + "status": status + }; + outputJSON["process_info"] = proc_info; + for (const entry of data.details) { + let date = entry["date"]; + if (entry["message"].startsWith("Ended ")) { + date += " "; + } + const row = [date, entry["type"], entry["activity"], outputMessage(entry["message"])]; + logLines.push(row); + } + }).fail(function(xhr, err) { + console.error("Error getting instance JSON: " + xhr.responseText); + }); + + $.ajax({ + type: "GET", + url: "/${base}/rest/logs/get?source=" + encodeURIComponent(JSON.stringify(baseEsReq)), + Accept : "application/json", + contentType: "application/json", + dataType: "json", + async: false + }).success(function(data) { + var finished = false; + scrollId = data._scroll_id; + if (data.hits) { + for (const hit of data.hits.hits) { + const source = hit._source; + const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; + logLines.push(row); + + } + } + while (!finished) { + $.ajax({ + type: "POST", + url: "/${base}/rest/logs/get/scroll", + data: "scrollId=" + scrollId, + async: false, + success: function(data) { + if (data.hits) { + + if (data.hits.hits.length > 0) { + for (const hit of data.hits.hits) { + const source = hit._source; + const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; + logLines.push(row); + } + scrollId = data._scroll_id; + } + else { + finished = true; + } + } + }, + error: function(e) { + alert("Error retrieving history data."); + } + }); + } + }).fail(function(xhr, err) { + console.error("Error getting instance JSON: " + xhr.responseText); + }); + logLines.sort(function(a, b) { + var aTemp = a[0]; + //if there is a space in the last char, remove it + if (aTemp.charAt(aTemp.length - 1) == " ") { + aTemp = aTemp.substring(0, aTemp.length - 1); + } + var bTemp = b[0]; + //if there is a space in the last char, remove it + if (bTemp.charAt(bTemp.length - 1) == " ") { + bTemp = bTemp.substring(0, bTemp.length - 1); + } + var aDate = moment(aTemp); + var bDate = moment(bTemp); + if (aDate.isBefore(bDate)) return -1; + if (bDate.isBefore(aDate)) return 1; + return 0; + }); + + var i = 0; + logLines.forEach(function(row) { + var data = row; + var tmpDetails = data[3]; + var details = ""; + var lineJson = {}; + var nestedJson = {}; + //go through data[0] and if there is a space at the end, remove it + if (data[0].charAt(data[0].length - 1) == " ") { + data[0] = data[0].substring(0, data[0].length - 1); + } + if (data[3].indexOf("Setting (json)") === -1) { + //check if data[3] starts with "
". If it does, remove it. + if (data[3].startsWith("
")) { + tmpDetails = data[3].substring(11); + } + details = data[3]; + lineJson = { + "time-stamp": data[0], + "type": data[1], + "source": data[2], + "details": details + }; + } else { + var fixedDetails = ""; + if (data[3].startsWith("
")) { + data[3] = data[3].substring(11); + } + //we need to first separate the string from the rest of the HTML + if (data[3].indexOf("_in =") !== -1) { + details = data[3].substring(0, data[3].indexOf("_in =")+3); + tmpDetails = data[3].substring(data[3].indexOf("_in =")+3); + } else { + details = data[3].substring(0, data[3].indexOf("_out =")+4); + tmpDetails = data[3].substring(data[3].indexOf("_out =")+4); + } + //now we need to go through and get details from json string + //note: key is always after ")+1); + var key = tmpDetails.substring(0, tmpDetails.indexOf("")); + tmpDetails = tmpDetails.substring(tmpDetails.indexOf("")); + nestedJson[key] = value; + } + //check/clean nested json object + if (nestedJson["stdout"] !== undefined) { + //replace all break points with new line + nestedJson["stdout"] = nestedJson["stdout"].replaceAll(/
/g, "\n"); + //find and remove everything between and + nestedJson["stdout"] = nestedJson["stdout"].replace(/.*<\/summary>/g, ""); + } + lineJson = { + "time-stamp": data[0], + "type": data[1], + "source": data[2], + "details": details, + "json": nestedJson + }; + } + //check/clean details + if (lineJson["details"] !== "") { + //replace all break points with new line + details = details.replaceAll('
', "\n"); + details = details.replaceAll('
', "\n"); + details = details.replaceAll("

", ""); + details = details.replaceAll("

", ""); + lineJson["details"] = details; + } + logLinesJSON[i] = lineJson; + i++; + } ); + outputJSON["logs"] = logLinesJSON; + return outputJSON; +}; + +function outputMessage(msg) { + + if (msg.startsWith("Setting (json) ")) { + + var i2 = msg.indexOf("= ") + + if (i2 != -1) { + var cmd = msg.substring(0, i2 + 1) + var jsonObj = JSON.parse(msg.substring(i2 + 2)) + var output = '
and value is the following td + while (tmpDetails.indexOf("
")+4); + var value = tmpDetails.substring(0, tmpDetails.indexOf("
' + cmd + '

' + + Object.keys(jsonObj).forEach(function(key) { + var value = jsonObj[key]; + output += makeRow(key, value, cmd) + }); + + output += '
' + + return output + } + } + + return msg.replace(//g, '>').replace(/\n/g, "
") +} + +function makeRow(key, value, cmd) { + + var style = 'width: 210px;' + + if (cmd.endsWith('_out =')) { + style = 'width: 120px;' + } + + if (key == 'stdout' || key == 'stderr') { + return '' + key + '' + formatMsg(value) + '' + } + return '' + key + '' + value + '' +} + +function formatMsg(msg) { + + var index = 0, count = 0, maxCount = 30 + + for ( ; count < maxCount && i2 != -1; count++) { + + var i2 = msg.indexOf('\n', index) + + if (i2 != -1) { + index = i2 + 1 + } + } + + if (count < maxCount - 1 || index > msg.length / 2) { + return msg.replace(//g, '>').replace(/\n/g, "
") + } + + var first = msg.substring(0, index).replace(//g, '>').replace(/\n/g, "
") + var rest = msg.substring(index).replace(//g, '>').replace(/\n/g, "
") + + return first + '
Show All' + rest + '
' +} + +function convertMillis(millis) { + + var x = millis / 1000 + var seconds = Math.floor(x % 60) + x /= 60 + var minutes = Math.floor(x) + + if (minutes === 0) + return millis / 1000 + " sec"; + + return minutes + " min " + seconds + " sec" +} + diff --git a/install/cws-ui/deployments.ftl b/install/cws-ui/deployments.ftl index a9388a8d..93121de2 100644 --- a/install/cws-ui/deployments.ftl +++ b/install/cws-ui/deployments.ftl @@ -11,8 +11,9 @@ + - - - @@ -18,6 +15,7 @@ + - - - - - - - - - + + + + + + + - <#include "navbar.ftl">
@@ -106,22 +94,80 @@

Processes

-
- By default, the 5000 most recent processes are loaded. - - - +
+

Filters:

+

Select filters before retrieving data to reduce loading time.

+
+

Process Definition:

+ +
+

Subprocess & Superprocess:

+ +
+
+ + +
+
+
+

Status:

+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+
+

Created Date:

+ + + +
+
+

Max Results:

+ + +
+
+
+
+
+ +
Matched Processes:
+
+
-
- - + +
+  Filters 
+

Displaying Subprocesses for Process Instance ID: 34374-349083748

-
@@ -137,7 +183,9 @@ Started on Worker Process Start Process End - Input Variables + Input Variables + Superprocess ID + UUID @@ -152,52 +200,236 @@ //STATE PERSISTANCE CONSTS const username = "username"; //temporary, hardcoded value for now - const rowsToLoadVar = "CWS_DASH_PROC_ROWS-" + username; + const hideSubProcsVar = "CWS_DASH_PROCS_HIDE_SUBPROCS-" + username; + //GLOBAL VARS var params = {}; - var rows; - var loadRows = 5000; - var rowsTotal = 0; + //DOCUMENT.READY START $( document ).ready(function() { - - //get our current url - var currentUrl = window.location.href; - - if (localStorage.getItem(rowsToLoadVar) != null) { - loadRows = parseInt(localStorage.getItem(rowsToLoadVar)); + //try to load hideSubProcsVar from local storage. If it doesn't exist, set it to true + //(automatically hides subprocs if never visited this page before) + if (localStorage.getItem(hideSubProcsVar) === null) { + localStorage.setItem(hideSubProcsVar, true); } - if (localStorage.getItem(rowsToLoadVar) != null) { - loadRows = parseInt(localStorage.getItem(rowsToLoadVar)); - } + //initialize our datepicker elements + $("#min-date").datepicker({ + orientation:'left top', + todayBtn: 'true', + todayHighlight:true + }); - displayMessage(); + $("#max-date").datepicker({ + orientation:'left top', + todayBtn: 'true', + todayHighlight:true + }); + + $("#filters-btn").click(function(){ + if($("#filters-div").is(":visible")) + $("#filter-arrow").removeClass("glyphicon-chevron-up").addClass("glyphicon-chevron-down"); + else + $("#filter-arrow").removeClass("glyphicon-chevron-down").addClass("glyphicon-chevron-up"); + $("#filters-div").slideToggle(); + }); + + $("#filter-submit-btn").click(function(){ + updateLocation(false); + }); + + $("#display-subprocs-div").css('display', 'none'); //get our params from the url params = getQueryString(); - if (!params) { - $("#hide-subprocs-btn").prop('checked', false); - $("#display-subprocs-div").css('display', 'none'); - } - else if (!params.superProcInstId) { - $("#hide-subprocs-btn").prop('checked', false); - $("#display-subprocs-div").css('display', 'none'); - } - else if (params.superProcInstId.toLowerCase() === 'null') { - $("#hide-subprocs-btn").prop('checked', true); - $("#display-subprocs-div").css('display', 'none'); - } - else { - $("#hide-subprocs-div").css('display', 'none'); - - $("#super-proc-inst-id").html(params.superProcInstId); - } + //get our current url + var currentUrl = window.location.href; + + //apply our params to the filter section of the page + applyParamsToFilters(params); + getNumMatchingProcesses(); + + //when we edit a filter in the filters table, get the new number of matching processes + $("#pd-select").change(function(){ + getNumMatchingProcesses(); + }); + $("#status-select").change(function(){ + getNumMatchingProcesses(); + }); + $("#min-date").change(function(){ + getNumMatchingProcesses(); + }); + $("#max-date").change(function(){ + getNumMatchingProcesses(); + }); + $("#super-proc-inst-id-in").change(function(){ + getNumMatchingProcesses(); + }); + $("#filter-submit-btn").click(function(){ + updateLocation(false); + }); + $("#hide-subprocs-btn").click(function(){ + updateLocation(true); + if (!($("#hide-subprocs-btn")).is(":checked")) { + $("#super-proc-inst-id-in").hide(); + } else { + $("#super-proc-inst-id-in").show(); + } + }); + $("#max-return-num").change(function(){ + getNumMatchingProcesses(); + }); + $("#max-return-all").change(function(){ + getNumMatchingProcesses(); + if ($("#max-return-all").is(":checked")) { + $("#max-return-num").prop('disabled', true); + } else { + $("#max-return-num").prop('disabled', false); + } + }); + + displayMessage(); $.fn.dataTable.moment( 'MMM D, YYYY, h:mm:ss A' ); $("#processes-table").DataTable({ + language: { + searchBuilder: { + add: "Add Criteria" + } + }, + deferRender: true, + columns: [ + { + data: null, + defaultContent: '', + className: 'select-checkbox', + orderable: false + }, + { + data: "procInstId", + defaultContent: '', + className: 'details-control', + orderable: false, + render: function (data, type) { + if (type === 'display') { + if (data == null) { + return 'History' + + "Subprocs"; + } + return 'History' + + "Subprocs"; + } + return data; + } + }, + { + data: "procDefKey", + }, + { + data: {procInstId : "procInstId", status : "status"}, + render: function (data, type) { + if (type === 'display') { + if (data.status === "incident") { + var incidentUrl = "/camunda/app/cockpit/default/#/process-instance/" + data.procInstId + "/runtime?tab=incidents-tab"; + return "" + data.procInstId + ""; + } else { + return data.procInstId; + } + } else { + return data; + } + } + }, + { data: "status" }, + { data: "createdTimestamp" }, + { + data: "startedByWorker", + render: function (data, type) { + if (type === 'display') { + if (data !== null) { + return data + "
Worker IP: " + data.split("_").slice(0, -2).join("."); + } + } + return data; + } + }, + { data: "procStartTime" }, + { + data: {procEndTime : "procEndTime", procStartTime : "procStartTime"}, + render: function (data, type) { + if (type === 'display') { + if (data.procEndTime == null) { + return ""; + } + if (data.procStartTime !== '' && data.procEndTime !== '') { + var start = moment(data.procStartTime); + var end = moment(data.procEndTime); + var procDuration = "
(~" + moment.duration(end.diff(start)).humanize() + ")"; + } else { + var procDuration = ''; + } + return data.procEndTime + procDuration; + } else { + return data.procEndTime; + } + } + }, + { + data: "inputVariables", + render: function (data, type) { + if (jQuery.isEmptyObject(data)) { + return ""; + } + if (type === 'display') { + var output = ""; + var before = ""; + var after = ""; + var putAllAfter = 0; + var count = 0; + for (const [key, value] of Object.entries(data)) { + if (key === "workerId") { + continue; + } + if (count > 3) { + putAllAfter = 1; + } + var temp = "
" + key + ": " + value + "
" + + "" + + "" + + "

"; + if (key === "startedOnWorkerId") { + after = after + temp; + putAllAfter = 1; + } else if (putAllAfter === 0) { + before = before + temp; + } else { + after = after + temp; + } + count++; + } + if (after.length < 0) { + output = before; + } else { + output = before + "
Show All" + after + "
"; + } + return output; + } else { + var outputToString = ""; + for (const [key, value] of Object.entries(data)) { + if (key === "workerId") { + continue; + } + outputToString += outputToString + key + ": " + value + ","; + } + return outputToString; + } + } + }, + { data: "superProcInstId" }, + { data: "uuid" } + ], searchDelay: 250, select: { style: 'multi+shift', @@ -206,23 +438,33 @@ columnDefs: [ { orderable: false, - className: 'select-checkbox', + className: 'select-checkbox noVis', targets: 0 }, { orderable: false, + className: 'noVis', searchable: false, targets: 1 }, { - orderable: false, - searchable: false, - targets: 2 - } + targets: [ 6,10,11 ], + visible: false + }, + { "width": "300px", "targets": 9 } ], - stateSave: true, - dom: "Q<'row'<'col-sm-auto buttons'B>><'row'<'col-sm-1 action-button'><'col-sm-1 download-button'><'col-sm-5 length'l><'col-sm-5 filter'f>>" + "tip", + "stateSave": true, + "stateLoadParams": function (settings, data) { + data.columns.forEach(function (column) { + column.search.search = ""; + }); + }, + dom: "Q<'row'<'col-sm-auto buttons'B>><'row'<'col-sm-1 action-button'><'col-sm-5 length'l><'col-sm-6 filter'f>>" + "tip", buttons: [ + { + extend: 'colvis', + columns: ':not(.noVis)' + }, { text: "Select all on page", action: function () { @@ -250,23 +492,13 @@ $("#processes-table").DataTable().rows().deselect(); updateActionList(); } - } + }, ], searchBuilder: { - columns: [3,4,5,6,7,8,9,10], - }, - language: { - searchBuilder: { - title: { - 0: 'Filters', - _: 'Filters (%d active)' - }, - } - } + columns: [2,3,4,5,6,7,8,9,10,11] + } }); - renderRows(loadRows); - var table = $("#processes-table").DataTable(); table.on( 'select', function ( e, dt, type, indexes ) { updateActionList(); @@ -276,12 +508,23 @@ updateActionList(); } ); + $(document).on('click', '.copy', function (e) { + e.preventDefault(); + var copyValue = $(this).attr('data-copyValue'); + copyInput(copyValue); + $(this).attr('aria-label', 'Copied!'); + setTimeout(function () { + $('.copy').attr('aria-label', 'Copy'); + }, 2000); + }); + $('' + '`).appendTo(".action-button"); - $(`)`).appendTo(".download-button"); + fetchAndDisplayProcesses(); }); + //DOCUMENT.READY END - $("#load-more-btn").click(function() { - loadRows += 5000; - localStorage.setItem(rowsToLoadVar, loadRows); - renderRows(loadRows); - }); + function fetchAndDisplayProcesses() { + //create our qstring + var qstring = "?"; + if(params != null){ + for(p in params){ + qstring += encodeURI(p)+"="+encodeURI(params[p])+"&"; + } + } + qstring = qstring.substring(0,qstring.length-1); + //fetch number of processes + var numProcs = 0; + //show ajax spinner + $(".ajax-spinner").show(); + $.ajax({ + url: "/${base}/rest/processes/getInstancesSize" + qstring, + type: "GET", + async: false, + success: function (data) { + numProcs = data; + }, + error: function (xhr, ajaxOptions, thrownError) { + console.log("Error getting number of processes: " + thrownError); + } + }); + //fetch processes + var numCalls = Math.ceil(numProcs / 50); + var returnedData = []; + var doneArr = []; + var urlPageAddition = ""; + if (qstring === "") { + urlPageAddition = "?page="; + } else { + urlPageAddition = "&page="; + } + for (var i = 0; i < numCalls; i++) { + $.ajax({ + url: "/${base}/rest/processes/getInstancesCamunda" + qstring + urlPageAddition + i, + type: "GET", + async: true, + success: function (data) { + returnedData = returnedData.concat(data); + doneArr.push(true); + }, + error: function (xhr, ajaxOptions, thrownError) { + console.log("Error getting processes: " + thrownError); + } + }); + } + var interval = setInterval(function () { + if (doneArr.length === numCalls) { + clearInterval(interval); + $("#processes-table").DataTable().clear(); + $("#processes-table").DataTable().rows.add(returnedData).draw(); + //hide ajax spinner + $(".ajax-spinner").hide(); + } + }, 250); + } + + function updateLocation(changeHideSubs) { + var localParams = {}; - $("#load-less-btn").click(function() { - loadRows -= 5000; - if (loadRows < 5000) { - loadRows = 5000; + if($("#pd-select").val() != "def"){ + localParams["procDefKey"] = $("#pd-select").val(); } - localStorage.setItem(rowsToLoadVar, loadRows); - renderRows(loadRows); - }); + localParams["status"] = ''; + $("#status-select input:checked").each(function(){ + localParams["status"] += $(this).val()+','; + }); + if(localParams["status"] != '') + localParams["status"] = localParams["status"].substr(0, localParams["status"].length - 1 ); + else + delete localParams['status']; + if($("#super-proc-inst-id-in").val() != ""){ + localParams["superProcInstId"] = $("#super-proc-inst-id-in").val(); + } + if($("#min-date").val() != ""){ + localParams["minDate"] = encodeURIComponent($("#min-date").val()); + } + if($("#max-date").val() != ""){ + localParams["maxDate"] = encodeURIComponent($("#max-date").val()); + } + if($("#max-return-all").prop("checked")){ + localParams["maxReturn"] = -1; + } + else{ + localParams["maxReturn"] = $("#max-return-num").val(); + } + if ($("#hide-subprocs-btn").prop("checked")) { + localParams["superProcInstId"] = "null"; + } else if (!($("#hide-subprocs-btn").prop("checked"))) { + delete localParams["superProcInstId"]; + } + var qstring = "?"; + if(localParams != null){ + for(p in localParams){ + qstring += encodeURI(p)+"="+encodeURI(localParams[p])+"&"; + } + } + qstring = qstring.substring(0,qstring.length-1); + console.log(encodeURI(qstring)); + window.location="/${base}/processes" + qstring; + } - $("#load-all-btn").click(function() { - loadRows = -1; - localStorage.setItem(rowsToLoadVar, loadRows); - renderRows(loadRows); - }); + function getNumMatchingProcesses() { + var localParams = {}; - function getFilterQString(changeHideSubs) { - var params = {}; - - if (changeHideSubs) { - if ($("#hide-subprocs-btn").prop("checked")) { - params.superProcInstId = "null"; + if($("#pd-select").val() != "def"){ + localParams["procDefKey"] = $("#pd-select").val(); + } + localParams["status"] = ''; + $("#status-select input:checked").each(function(){ + localParams["status"] += $(this).val()+','; + }); + if(localParams["status"] != '') + localParams["status"] = localParams["status"].substr(0, localParams["status"].length - 1 ); + else + delete localParams['status']; + if($("#super-proc-inst-id-in").val() != ""){ + localParams["superProcInstId"] = $("#super-proc-inst-id-in").val(); + } + if($("#min-date").val() != ""){ + localParams["minDate"] = encodeURIComponent($("#min-date").val()); + } + if($("#max-date").val() != ""){ + localParams["maxDate"] = encodeURIComponent($("#max-date").val()); + } + if($("#max-return-all").prop("checked")){ + localParams["maxReturn"] = -1; + } + else{ + localParams["maxReturn"] = $("#max-return-num").val(); + } + if ($("#hide-subprocs-btn").prop("checked")) { + localParams["superProcInstId"] = "null"; + } else { + delete localParams["superProcInstId"]; + + } + var qstring = "?"; + if(localParams != null){ + for(p in localParams){ + qstring += encodeURI(p)+"="+encodeURI(localParams[p])+"&"; } } - else { - var qs = getQueryString(); - - if (qs && qs.superProcInstId) { - params.superProcInstId = qs.superProcInstId; + qstring = qstring.substring(0,qstring.length-1); + var numMatching = 0; + $.ajax({ + url: "/${base}/rest/processes/getInstancesSize" + qstring, + type: "GET", + async: false, + success: function (data) { + numMatching = data; + }, + error: function (xhr, ajaxOptions, thrownError) { + console.log("Error getting number of processes: " + thrownError); + numMatching = "Error"; } + }); + $("#numMatchProcesses").text(numMatching); + if (numMatching > 5000) { + $("#procCountWarning").text("Warning: Large number of processes may increase load time."); + } else { + $("#procCountWarning").text(""); } - - var qstring = "?"; + } + function applyParamsToFilters(params) { if(params != null){ - for(p in params){ - qstring += encodeURI(p)+"="+encodeURI(params[p])+"&"; + $("#pd-select").val(params.procDefKey || "def"); + if(params.status){ + var k = params.status.split(','); + for(i in k){ + $("#status-select input[value='"+k[i]+"']").prop("checked",true); + } + } + if (!params) { + $("#super-proc-inst-id-in").show(); + $("#hide-subprocs-btn").prop('checked', false); + $("#display-subprocs-div").css('display', 'none'); } + else if (params.superProcInstId == undefined) { + $("#super-proc-inst-id-in").show(); + $("#hide-subprocs-btn").prop('checked', false); + $("#display-subprocs-div").css('display', 'none'); + } + else if (params.superProcInstId.toLowerCase() === 'null') { + $("#super-proc-inst-id-in").hide(); + $("#hide-subprocs-btn").prop('checked', true); + $("#display-subprocs-div").css('display', 'none'); + } + else { + $("#super-proc-inst-id-in").show(); + $("#hide-subprocs-div").css('display', 'none'); + $("#super-proc-inst-id").html(params.superProcInstId); + } + //$("#status-select").val(params.status); + $("#min-date").val(params.minDate || ""); + $("#max-date").val(params.maxDate || ""); + $("#max-return").val(params.maxReturn || 5000); + $("#super-proc-inst-id-in").val(params.superProcInstId || ""); } - qstring = qstring.substring(0,qstring.length-1); - return qstring; } - function updateLocation(changeHideSubs) { - window.location="/${base}/processes" + getFilterQString(changeHideSubs); - } - - $("#hide-subprocs-btn").click(function(){ - updateLocation(true); - }); - // --------------------------------- // DISPLAY STATUS MESSAGE (IF ANY) // @@ -374,8 +759,6 @@ } } - - function viewHistory(procInstId) { if (procInstId !== '') { @@ -393,97 +776,6 @@ return false; } } - - // ---------------------------------------------------- - // Get the process instances, and render them as rows - // - function renderRows(rowsToLoad) { - - $("#proc-log div.ajax-spinner").show(); - - qstr = document.location.search; - //we only care about the superProcInstId part of query string - if (qstr.indexOf("superProcInstId") > -1) { - //get everything after ? and before first & - qstr = qstr.substring(qstr.indexOf("?")); - if (qstr.indexOf("&") > -1) { - qstr = qstr.substring(0, qstr.indexOf("&")); - } - } else { - qstr = ""; - } - //console.log("/${base}/rest/processes/getInstances"+qstr); - params = getQueryString(); - var numProcs = 0; - var requestProc = 0; - - //get the number of instances - $.get("/${base}/rest/processes/getInstancesSize"+qstr, - function(res) { - numProcs = res; - rowsTotal = res; - if (numProcs < 5000) { - $("#load-more-div").hide(); - } else { - $("#load-more-div").show(); - } - if (rowsToLoad === -1) { - requestProc = numProcs; - } else { - requestProc = rowsToLoad; - } - // - // GET THE PROCESS INSTANCE, - // AND RENDER THEM... - // - if (qstr === "") { - qstr = "?"; - } - $.get("/${base}/rest/processes/getInstancesCamunda"+qstr+"&page="+requestProc, - function(res) { - var table = $("#processes-table").DataTable() - - table.clear(); - for (i in res) { - var procInstId = (res[i].procInstId == undefined ? '' : res[i].procInstId); - var incidentUrl = "/camunda/app/cockpit/default/#/process-instance/" + procInstId + "/runtime?tab=incidents-tab"; - if (res[i].startedByWorker !== undefined) { - var workerIP = "
Worker IP: " + res[i].startedByWorker.split("_").slice(0, -2).join("."); - } else { - var workerIP = "
Worker IP: "; - } - var procStartTime = (res[i].procStartTime == undefined ? '' : res[i].procStartTime); - var procEndTime = (res[i].procEndTime == undefined ? '' : res[i].procEndTime); - if (procStartTime !== '' && procEndTime !== '') { - var start = moment(procStartTime); - var end = moment(procEndTime); - var procDuration = "
(~" + moment.duration(end.diff(start)).humanize() + ")"; - } else { - var procDuration = ''; - } - table.row.add( - $(""+ - "" + - "History" + - "Subprocs" + - ""+ res[i].procDefKey +""+ - ""+ (res[i].status == 'incident' ? ("" + procInstId + "") : procInstId) + "" + - ""+ res[i].status +""+ - ""+ (res[i].createdTimestamp == undefined ? '' : res[i].createdTimestamp) + ""+ - ""+ (res[i].startedByWorker == undefined ? '' : res[i].startedByWorker + workerIP) + ""+ - ""+ procStartTime + ""+ - ""+ procEndTime + procDuration + ""+ - ""+ (res[i].inputVariables == undefined ? '' : res[i].inputVariables) + ""+ - "") - ); - } - table.draw(); - - $("#proc-log div.ajax-spinner").hide(); - }); - }); - - } // --------------------------------------------------------------- // Updates the list of active items in the Actions drop-down list @@ -505,7 +797,7 @@ selectedRows.every( function ( rowIdx, tableLoop, rowLoop ) { var data = this.data(); - switch (data[6]) { + switch (data[4]) { case 'disabled': numDisabledSelected++; break; @@ -554,6 +846,8 @@ $("#action_download_selected_json").removeClass("enabled"); $("#action_download_selected_csv").addClass("disabled"); $("#action_download_selected_csv").removeClass("enabled"); + $("#action_download_selected_list").addClass("disabled"); + $("#action_download_selected_list").removeClass("enabled"); // Remove hrefs from the anchor tags $("#action_disable_atag").removeAttr("href"); @@ -565,6 +859,7 @@ $("#action_copy_all_selected_history_links_atag").removeAttr("href"); $("#action_download_selected_json_atag").removeAttr("href"); $("#action_download_selected_csv_atag").removeAttr("href"); + $("#action_download_selected_list_atag").removeAttr("href"); // Enable the right one @@ -594,15 +889,19 @@ $("#action_mark_as_resolved_atag").attr("href", "javascript:action_mark_as_resolved();"); } - if ((numSelected > 0 && numPendingSelected === 0)) { - $("#action_open_selected_new_tabs").removeClass("disabled"); - $("#action_open_selected_new_tabs_atag").attr("href", "javascript:action_open_selected_new_tabs();"); - $("#action_copy_all_selected_history_links").removeClass("disabled"); - $("#action_copy_all_selected_history_links_atag").attr("href", "javascript:action_copy_all_selected_history_links();"); - $("#action_download_selected_json").removeClass("disabled"); - $("#action_download_selected_json_atag").attr("href", "javascript:downloadSelectedJSON();"); - $("#action_download_selected_csv").removeClass("disabled"); - $("#action_download_selected_csv_atag").attr("href", "javascript:downloadSelectedCSV();"); + if ((numSelected > 0)) { + $("#action_download_selected_list").removeClass("disabled"); + $("#action_download_selected_list_atag").attr("href", "javascript:downloadListJSON();"); + if (numPendingSelected === 0) { + $("#action_open_selected_new_tabs").removeClass("disabled"); + $("#action_open_selected_new_tabs_atag").attr("href", "javascript:action_open_selected_new_tabs();"); + $("#action_copy_all_selected_history_links").removeClass("disabled"); + $("#action_copy_all_selected_history_links_atag").attr("href", "javascript:action_copy_all_selected_history_links();"); + $("#action_download_selected_json").removeClass("disabled"); + $("#action_download_selected_json_atag").attr("href", "javascript:downloadSelectedJSON();"); + $("#action_download_selected_csv").removeClass("disabled"); + $("#action_download_selected_csv_atag").attr("href", "javascript:downloadSelectedCSV();"); + } } // Execute adaptation actions if any @@ -614,7 +913,7 @@ var selectedRows = table.rows( { selected: true } ); selectedRows.every( function ( rowIdx, tableLoop, rowLoop ) { var data = this.data(); - window.open("/${base}/history?procInstId=" + data[5], "_blank"); + window.open("/${base}/history?procInstId=" + data["procInstId"], "_blank"); } ); } @@ -626,16 +925,16 @@ var links = ""; selectedRows.every( function ( rowIdx, tableLoop, rowLoop ) { var data = this.data(); - links += protocol + "://" + host + "/${base}/history?procInstId=" + data[5] + "\n"; + links += protocol + "://" + host + "/${base}/history?procInstId=" + data["procInstId"] + "\n"; } ); navigator.clipboard.writeText(links); } - // ------------------------------------------------------------------------------- // Function fired when user clicks on "Enable Selected Rows..." in drop-down list // function action_enable_rows() { + var table = $("#processes-table").DataTable(); $.ajax({ type: "POST", url: "/${base}/rest/processes/makeDisabledRowsPending", @@ -646,19 +945,18 @@ }) .done(function(msg) { $("#action_msg").html(msg.message); - renderRows(); - resetCheckboxes(); + table.ajax.reload(); }) .fail(function(xhr, err) { $("#action_msg").html(xhr.responseTextmsg.message); }); } - // -------------------------------------------------------------------------------- // Function fired when user clicks on "Disable Selected Rows..." in drop-down list // function action_disable_rows() { + var table = $("#processes-table").DataTable(); $.ajax({ type: "POST", url: "/${base}/rest/processes/makePendingRowsDisabled", @@ -669,19 +967,18 @@ }) .done(function(msg) { $("#action_msg").html(msg.message); - renderRows(); - resetCheckboxes(); + table.ajax.reload(); }) .fail(function(xhr, err) { $("#action_msg").html(xhr.responseTextmsg.message); }); } - // -------------------------------------------------------------------------------- // Function fired when user clicks on "Retry Selected Incident Rows..." in drop-down list // function action_retry_incident_rows() { + var table = $("#processes-table").DataTable(); $.ajax({ type: "POST", url: "/${base}/rest/processes/retryIncidentRows", @@ -692,7 +989,7 @@ }) .done(function(msg) { $("#action_msg").html(msg.message); - location.reload(); + table.ajax.reload(); }) .fail(function(xhr, err) { $("#action_msg").html(xhr.responseTextmsg.message); @@ -707,6 +1004,7 @@ // Function fired when user clicks on "Retry Selected Failed to Start Rows..." in drop-down list // function action_retry_failed_to_start() { + var table = $("#processes-table").DataTable(); $.ajax({ type: "POST", url: "/${base}/rest/processes/retryFailedToStart", @@ -717,7 +1015,7 @@ }) .done(function(msg) { $("#action_msg").html(msg.message); - location.reload(); + table.ajax.reload(); }) .fail(function(xhr, err) { $("#action_msg").html(xhr.responseTextmsg.message); @@ -728,6 +1026,7 @@ // Function fired when user clicks on "Mark Selected Failed Rows As Resolved..." in drop-down list // function action_mark_as_resolved() { + var table = $("#processes-table").DataTable(); $.ajax({ type: "POST", url: "/${base}/rest/processes/markResolved", @@ -738,20 +1037,12 @@ }) .done(function(msg) { $("#action_msg").html(msg.message); - location.reload(); + table.ajax.reload(); }) .fail(function(xhr, err) { $("#action_msg").html(xhr.responseTextmsg.message); }); } - - // ------------------------------------ - // Clear checked selections (resolves timing issues after an action is chosen) - // - function resetCheckboxes() { - $("input[status]:checked").attr("checked", false) - updateActionList(); - } // ------------------------------------ // Get array of selected rows (uuids) @@ -765,11 +1056,10 @@ var selectedRows = table.rows( { selected: true } ); selectedRows.every( function ( rowIdx, tableLoop, rowLoop ) { - var html = this.node(); - var status = $(html).children("td:first").attr("status"); - var uuid = $(html).children("td:first").attr("uuid"); - var procInstId = $(html).children("td:first").attr("procInstId"); var data = this.data(); + var status = data["status"]; + var uuid = data["uuid"]; + var procInstId = data["procInstId"]; switch (status) { case 'disabled': selectedRowUuids.push(uuid); @@ -798,7 +1088,7 @@ var selectedRows = table.rows( { selected: true } ); selectedRows.every( function ( rowIdx, tableLoop, rowLoop ) { var data = this.data(); - var procInstId = data[5]; + var procInstId = data["procInstId"]; var json = getInstanceJSON(procInstId); mainJSON[procInstId] = json; }); @@ -815,7 +1105,7 @@ var selectedRows = table.rows( { selected: true } ); selectedRows.every( function ( rowIdx, tableLoop, rowLoop ) { var data = this.data(); - var procInstId = data[5]; + var procInstId = data["procInstId"]; var csv = getInstanceCSV(procInstId); mainCSV += csv; }); @@ -825,452 +1115,6 @@ ); } - function getInstanceCSV(procInstId) { - var outputCSV = ""; - var logLines = []; - var scrollId = ""; - var proc_info = {}; - var baseEsReq = { - "from": 0, - "size": 20, - "query": { - "bool": { - "must" :[] - } - }, - "sort": { "@timestamp": { "order": "asc" } } - }; - baseEsReq.query.bool.must.push({"query_string":{"fields":["procInstId"],"query" : "\"" + decodeURIComponent(procInstId) + "\""}}); - - //get process history - $.ajax({ - type: "GET", - url: "/${base}/rest/history/" + procInstId, - Accept : "application/json", - contentType: "application/json", - dataType: "json", - async: false - }).success(function(data) { - var status = data.state; - if (data.state === "COMPLETED") { - status = "Complete"; - } - else if (data.state === "ACTIVE") { - status = "Running"; - } - proc_info["process_definition"] = data.procDefKey; - proc_info["process_instance"] = data.procInstId; - proc_info["start_time"] = data.startTime; - proc_info["end_time"] = data.endTime; - proc_info["duration"] = convertMillis(data.duration); - proc_info["status"] = status; - for (const entry of data.details) { - let date = entry["date"]; - if (entry["message"].startsWith("Ended ")) { - date += " "; - } - const row = [date, entry["type"], entry["activity"], outputMessage(entry["message"])]; - logLines.push(row); - } - }).fail(function(xhr, err) { - console.error("Error getting instance JSON: " + xhr.responseText); - }); - - $.ajax({ - type: "GET", - url: "/${base}/rest/logs/get?source=" + encodeURIComponent(JSON.stringify(baseEsReq)), - Accept : "application/json", - contentType: "application/json", - dataType: "json", - async: false - }).success(function(data) { - var finished = false; - scrollId = data._scroll_id; - if (data.hits) { - for (const hit of data.hits.hits) { - const source = hit._source; - const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; - logLines.push(row); - - } - } - while (!finished) { - $.ajax({ - type: "POST", - url: "/${base}/rest/logs/get/scroll", - data: "scrollId=" + scrollId, - async: false, - success: function(data) { - if (data.hits) { - - if (data.hits.hits.length > 0) { - for (const hit of data.hits.hits) { - const source = hit._source; - const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; - logLines.push(row); - } - scrollId = data._scroll_id; - } - else { - finished = true; - } - } - }, - error: function(e) { - alert("Error retrieving history data."); - } - }); - } - }).fail(function(xhr, err) { - console.error("Error getting instance JSON: " + xhr.responseText); - }); - logLines.sort(function(a, b) { - var aTemp = a[0]; - //if there is a space in the last char, remove it - if (aTemp.charAt(aTemp.length - 1) == " ") { - aTemp = aTemp.substring(0, aTemp.length - 1); - } - var bTemp = b[0]; - //if there is a space in the last char, remove it - if (bTemp.charAt(bTemp.length - 1) == " ") { - bTemp = bTemp.substring(0, bTemp.length - 1); - } - var aDate = moment(aTemp); - var bDate = moment(bTemp); - if (aDate.isBefore(bDate)) return -1; - if (bDate.isBefore(aDate)) return 1; - return 0; - }); - - logLines.forEach(function(row) { - var data = row; - var details = data[3]; - var tmpDetails = ""; - var lineString = ""; - if (data[3].indexOf("Setting (json)") === -1) { - details = details.replaceAll('
', "\n"); - details = details.replaceAll("

", ""); - details = details.replaceAll("

", ""); - details = details.replaceAll('"' , '""'); - details = details.replaceAll('\n' , ' '); - //add first and last char as double quotes - details = '"' + details + '"'; - lineString = proc_info["process_definition"] + "," + proc_info["process_instance"] + "," + data[0] + "," + data[1] + "," + data[2] + "," + details + "\r\n"; - } else { - lineString = proc_info["process_definition"] + "," + proc_info["process_instance"] + "," + data[0] + "," + data[1] + "," + data[2] + ","; - //remove last char - if (data[3].indexOf("_in =") !== -1) { - lineString += '"' + details.substring(0, details.indexOf("_in =")+3) + " "; - details = details.substring(details.indexOf("_in =")+3); - } else { - lineString += '"' + details.substring(0, details.indexOf("_out =")+4) + " "; - details = details.substring(details.indexOf("_out =")+4); - } - //now we need to go through and get details from json string - //note: key is always after and value is the following td - while (details.indexOf("")+1); - var key = details.substring(0, details.indexOf("")); - details = details.substring(details.indexOf("")+4); - var value = details.substring(0, details.indexOf("")); - tmpDetails += key + ": " + value + "; "; - } - //check/clean tmpDetails - if (tmpDetails !== "") { - //replace all break points with new line - tmpDetails = tmpDetails.replaceAll(/
/g, " "); - //find and remove everything between and - tmpDetails = tmpDetails.replace(/.*<\/summary>/g, ""); - //find and remove
and
- tmpDetails = tmpDetails.replace(/
/g, ""); - tmpDetails = tmpDetails.replace(/<\/details>/g, ""); - //CSV quirk: replace all " with "" - tmpDetails = tmpDetails.replaceAll('"' , '""'); - } - //remove last char - tmpDetails = tmpDetails.substring(0, tmpDetails.length-1); - tmpDetails = tmpDetails + '"'; - lineString += tmpDetails + "\r\n"; - } - lineString = lineString.replaceAll("", ""); - outputCSV = outputCSV + lineString; - } ); - return outputCSV; - }; - - function getInstanceJSON(procInstId) { - var outputJSON = {}; - var logLinesJSON = {}; - var logLines = []; - var scrollId = ""; - var baseEsReq = { - "from": 0, - "size": 20, - "query": { - "bool": { - "must" :[] - } - }, - "sort": { "@timestamp": { "order": "asc" } } - }; - baseEsReq.query.bool.must.push({"query_string":{"fields":["procInstId"],"query" : "\"" + decodeURIComponent(procInstId) + "\""}}); - - //get process history - $.ajax({ - type: "GET", - url: "/${base}/rest/history/" + procInstId, - Accept : "application/json", - contentType: "application/json", - dataType: "json", - async: false - }).success(function(data) { - var status = data.state; - if (data.state === "COMPLETED") { - status = "Complete"; - } - else if (data.state === "ACTIVE") { - status = "Running"; - } - var proc_info = { - "process_definition": data.procDefKey, - "process_instance": data.procInstId, - "start_time": data.startTime, - "end_time": data.endTime, - "duration": convertMillis(data.duration), - "status": status - }; - outputJSON["process_info"] = proc_info; - for (const entry of data.details) { - let date = entry["date"]; - if (entry["message"].startsWith("Ended ")) { - date += " "; - } - const row = [date, entry["type"], entry["activity"], outputMessage(entry["message"])]; - logLines.push(row); - } - }).fail(function(xhr, err) { - console.error("Error getting instance JSON: " + xhr.responseText); - }); - - $.ajax({ - type: "GET", - url: "/${base}/rest/logs/get?source=" + encodeURIComponent(JSON.stringify(baseEsReq)), - Accept : "application/json", - contentType: "application/json", - dataType: "json", - async: false - }).success(function(data) { - var finished = false; - scrollId = data._scroll_id; - if (data.hits) { - for (const hit of data.hits.hits) { - const source = hit._source; - const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; - logLines.push(row); - - } - } - while (!finished) { - $.ajax({ - type: "POST", - url: "/${base}/rest/logs/get/scroll", - data: "scrollId=" + scrollId, - async: false, - success: function(data) { - if (data.hits) { - - if (data.hits.hits.length > 0) { - for (const hit of data.hits.hits) { - const source = hit._source; - const row = [source["@timestamp"], "Log", source.actInstId.split(':')[0], "

" + source.msgBody.replace(//g, '>').replace(/\n/g, "
") + "

"]; - logLines.push(row); - } - scrollId = data._scroll_id; - } - else { - finished = true; - } - } - }, - error: function(e) { - alert("Error retrieving history data."); - } - }); - } - }).fail(function(xhr, err) { - console.error("Error getting instance JSON: " + xhr.responseText); - }); - logLines.sort(function(a, b) { - var aTemp = a[0]; - //if there is a space in the last char, remove it - if (aTemp.charAt(aTemp.length - 1) == " ") { - aTemp = aTemp.substring(0, aTemp.length - 1); - } - var bTemp = b[0]; - //if there is a space in the last char, remove it - if (bTemp.charAt(bTemp.length - 1) == " ") { - bTemp = bTemp.substring(0, bTemp.length - 1); - } - var aDate = moment(aTemp); - var bDate = moment(bTemp); - if (aDate.isBefore(bDate)) return -1; - if (bDate.isBefore(aDate)) return 1; - return 0; - }); - - var i = 0; - logLines.forEach(function(row) { - var data = row; - var tmpDetails = data[3]; - var details = ""; - var lineJson = {}; - var nestedJson = {}; - //go through data[0] and if there is a space at the end, remove it - if (data[0].charAt(data[0].length - 1) == " ") { - data[0] = data[0].substring(0, data[0].length - 1); - } - if (data[3].indexOf("Setting (json)") === -1) { - //check if data[3] starts with "
". If it does, remove it. - if (data[3].startsWith("
")) { - tmpDetails = data[3].substring(11); - } - details = data[3]; - lineJson = { - "time-stamp": data[0], - "type": data[1], - "source": data[2], - "details": details - }; - } else { - var fixedDetails = ""; - if (data[3].startsWith("
")) { - data[3] = data[3].substring(11); - } - //we need to first separate the string from the rest of the HTML - if (data[3].indexOf("_in =") !== -1) { - details = data[3].substring(0, data[3].indexOf("_in =")+3); - tmpDetails = data[3].substring(data[3].indexOf("_in =")+3); - } else { - details = data[3].substring(0, data[3].indexOf("_out =")+4); - tmpDetails = data[3].substring(data[3].indexOf("_out =")+4); - } - //now we need to go through and get details from json string - //note: key is always after ")+1); - var key = tmpDetails.substring(0, tmpDetails.indexOf("")); - tmpDetails = tmpDetails.substring(tmpDetails.indexOf("")); - nestedJson[key] = value; - } - //check/clean nested json object - if (nestedJson["stdout"] !== undefined) { - //replace all break points with new line - nestedJson["stdout"] = nestedJson["stdout"].replaceAll(/
/g, "\n"); - //find and remove everything between and - nestedJson["stdout"] = nestedJson["stdout"].replace(/.*<\/summary>/g, ""); - } - lineJson = { - "time-stamp": data[0], - "type": data[1], - "source": data[2], - "details": details, - "json": nestedJson - }; - } - //check/clean details - if (lineJson["details"] !== "") { - //replace all break points with new line - details = details.replaceAll('
', "\n"); - details = details.replaceAll('
', "\n"); - details = details.replaceAll("

", ""); - details = details.replaceAll("

", ""); - lineJson["details"] = details; - } - logLinesJSON[i] = lineJson; - i++; - } ); - outputJSON["logs"] = logLinesJSON; - return outputJSON; - }; - - function outputMessage(msg) { - - if (msg.startsWith("Setting (json) ")) { - - var i2 = msg.indexOf("= ") - - if (i2 != -1) { - var cmd = msg.substring(0, i2 + 1) - var jsonObj = JSON.parse(msg.substring(i2 + 2)) - var output = '
and value is the following td - while (tmpDetails.indexOf("
")+4); - var value = tmpDetails.substring(0, tmpDetails.indexOf("
' + cmd + '

' - - Object.keys(jsonObj).forEach(function(key) { - var value = jsonObj[key]; - output += makeRow(key, value, cmd) - }); - - output += '
' - - return output - } - } - - return msg.replace(//g, '>').replace(/\n/g, "
") - } - - function makeRow(key, value, cmd) { - - var style = 'width: 210px;' - - if (cmd.endsWith('_out =')) { - style = 'width: 120px;' - } - - if (key == 'stdout' || key == 'stderr') { - return '' + key + '' + formatMsg(value) + '' - } - return '' + key + '' + value + '' - } - - function formatMsg(msg) { - - var index = 0, count = 0, maxCount = 30 - - for ( ; count < maxCount && i2 != -1; count++) { - - var i2 = msg.indexOf('\n', index) - - if (i2 != -1) { - index = i2 + 1 - } - } - - if (count < maxCount - 1 || index > msg.length / 2) { - return msg.replace(//g, '>').replace(/\n/g, "
") - } - - var first = msg.substring(0, index).replace(//g, '>').replace(/\n/g, "
") - var rest = msg.substring(index).replace(//g, '>').replace(/\n/g, "
") - - return first + '
Show All' + rest + '
' - } - - function convertMillis(millis) { - - var x = millis / 1000 - var seconds = Math.floor(x % 60) - x /= 60 - var minutes = Math.floor(x) - - if (minutes === 0) - return millis / 1000 + " sec"; - - return minutes + " min " + seconds + " sec" - } - $("#json-bttn").click(function(e) { e.preventDefault(); downloadListJSON(); @@ -1282,108 +1126,58 @@ var numRows = dt.rows({selected:true}).count(); var jsonFile = {}; var processes = {}; - jsonFile["num_rows_loaded"] = rowsTotal; - - if (numRows === 0) { - dt.rows({search:'applied'}).every( function ( rowIdx, tableLoop, rowLoop ) { - var data = this.data(); - var thisProcJSON = {}; - var startedOnWorker = ""; - var workerIP = ""; - var duration = ""; - var process_end = ""; - var inputVars = ""; - var inputVarsTemp = ""; - - if (data[8] !== "") { - startedOnWorker = data[6].substring(0, data[6].indexOf("
")); - workerIP = data[6].substring(data[6].indexOf("")+4, data[6].length); - } else { - startedOnWorker = data[6]; - } - if (data[8] !== "") { - duration = data[8]; - //get everything after
but before - duration = duration.substring(duration.indexOf("
") + 7, duration.indexOf("")); - process_end = data[8].substring(0, data[8].indexOf("
")-1); - } - - if (data[9] !== "") { - inputVarsTemp = data[9].replaceAll("
", ", "); - inputVarsTemp = inputVarsTemp.replaceAll("
Show All", ""); - while (inputVarsTemp.indexOf("") !== -1) { - inputVarsTemp = inputVarsTemp.substring(inputVarsTemp.indexOf("") + 3, inputVarsTemp.length); - inputVarsTemp = inputVarsTemp.replace("", "") - inputVars += inputVarsTemp.substring(0, inputVarsTemp.indexOf("
")) + ", "; - } - inputVars = inputVars.substring(0, inputVars.length - 2); - } + dt.rows({selected: true, search:'applied'}).every( function ( rowIdx, tableLoop, rowLoop ) { + var data = this.data(); + console.log(data); + var thisProcJSON = {}; + var startedOnWorker = ""; + var workerIP = ""; + var duration = ""; + var process_end = ""; + var inputVars = ""; + var inputVarsTemp = ""; + + if (data["startedByWorker"] !== "") { + startedOnWorker = data["startedByWorker"]; + workerIP = data["startedByWorker"].split("_").slice(0, -2).join("."); + } else { + startedOnWorker = data["startedByWorker"]; + } - thisProcJSON["definition_key"] = data[2]; - thisProcJSON["process_instance_id"] = data[3]; - thisProcJSON["status"] = data[4]; - thisProcJSON["schedule_queued_time"] = data[5]; - thisProcJSON["started_on_worker"] = startedOnWorker; - thisProcJSON["worker_ip"] = workerIP; - thisProcJSON["process_start"] = data[7]; - thisProcJSON["process_end"] = process_end; - thisProcJSON["duration"] = duration; - thisProcJSON["input_variables"] = inputVars; - - processes[data[3]] = thisProcJSON; - } ); - } else { - dt.rows({selected: true, search:'applied'}).every( function ( rowIdx, tableLoop, rowLoop ) { - var data = this.data(); - var thisProcJSON = {}; - var startedOnWorker = ""; - var workerIP = ""; - var duration = ""; - var process_end = ""; - var inputVars = ""; - var inputVarsTemp = ""; - - if (data[8] !== "") { - startedOnWorker = data[6].substring(0, data[6].indexOf("
")); - workerIP = data[6].substring(data[6].indexOf("")+4, data[6].length); + if (data["procEndTime"] !== "") { + process_end = data["procEndTime"]; + if (data["procStartTime"] !== '' && data["procEndTime"] !== '') { + var start = moment(data["procStartTime"]); + var end = moment(data["procEndTime"]); + duration = moment.duration(end.diff(start)).humanize(); } else { - startedOnWorker = data[6]; + duration = ''; } + } - if (data[8] !== "") { - duration = data[8]; - //get everything after
but before - duration = duration.substring(duration.indexOf("
") + 7, duration.indexOf("")); - process_end = data[8].substring(0, data[8].indexOf("
")-1); - + if (data["inputVariables"] !== {}) { + for (var key in data["inputVariables"]) { + inputVarsTemp += key + ": " + data["inputVariables"][key] + ", "; } - - if (data[9] !== "") { - inputVarsTemp = data[9].replaceAll("
", ", "); - inputVarsTemp = inputVarsTemp.replaceAll("
Show All", ""); - while (inputVarsTemp.indexOf("") !== -1) { - inputVarsTemp = inputVarsTemp.substring(inputVarsTemp.indexOf("") + 3, inputVarsTemp.length); - inputVarsTemp = inputVarsTemp.replace("", "") - inputVars += inputVarsTemp.substring(0, inputVarsTemp.indexOf("
")) + ", "; - } - inputVars = inputVars.substring(0, inputVars.length - 2); + if (inputVarsTemp.length > 2) { + inputVars = inputVarsTemp.substring(0, inputVarsTemp.length-2); } + } - thisProcJSON["definition_key"] = data[2]; - thisProcJSON["process_instance_id"] = data[3]; - thisProcJSON["status"] = data[4]; - thisProcJSON["schedule_queued_time"] = data[5]; - thisProcJSON["started_on_worker"] = startedOnWorker; - thisProcJSON["worker_ip"] = workerIP; - thisProcJSON["process_start"] = data[7]; - thisProcJSON["process_end"] = process_end; - thisProcJSON["duration"] = duration; - thisProcJSON["input_variables"] = inputVars; - - processes[data[3]] = thisProcJSON; - } ); - } + thisProcJSON["definition_key"] = data["procDefKey"]; + thisProcJSON["process_instance_id"] = data["procInstId"]; + thisProcJSON["status"] = data["status"]; + thisProcJSON["schedule_queued_time"] = data["createdTimestamp"]; + thisProcJSON["started_on_worker"] = startedOnWorker; + thisProcJSON["worker_ip"] = workerIP; + thisProcJSON["process_start"] = data["procStartTime"]; + thisProcJSON["process_end"] = process_end; + thisProcJSON["duration"] = duration; + thisProcJSON["input_variables"] = inputVars; + + processes[data["procInstId"]] = thisProcJSON; + } ); jsonFile["processes"] = processes; console.log(jsonFile); $.fn.dataTable.fileSave(