public void updateJobOutput(
     String jobId, ExecutableState newStatus, Map<String, String> info, String output) {
   try {
     final ExecutableOutputPO jobOutput = executableDao.getJobOutput(jobId);
     Preconditions.checkArgument(
         jobOutput != null, "there is no related output for job id:" + jobId);
     ExecutableState oldStatus = ExecutableState.valueOf(jobOutput.getStatus());
     if (newStatus != null && oldStatus != newStatus) {
       if (!ExecutableState.isValidStateTransfer(oldStatus, newStatus)) {
         throw new IllegalStateTranferException(
             "there is no valid state transfer from:" + oldStatus + " to:" + newStatus);
       }
       jobOutput.setStatus(newStatus.toString());
     }
     if (info != null) {
       jobOutput.setInfo(info);
     }
     if (output != null) {
       jobOutput.setContent(output);
     }
     executableDao.updateJobOutput(jobOutput);
     logger.info("job id:" + jobId + " from " + oldStatus + " to " + newStatus);
   } catch (PersistentException e) {
     logger.error("error change job:" + jobId + " to " + newStatus.toString());
     throw new RuntimeException(e);
   }
 }
 private DefaultOutput parseOutput(ExecutableOutputPO jobOutput) {
   final DefaultOutput result = new DefaultOutput();
   result.setExtra(jobOutput.getInfo());
   result.setState(ExecutableState.valueOf(jobOutput.getStatus()));
   result.setVerboseMsg(jobOutput.getContent());
   result.setLastModified(jobOutput.getLastModified());
   return result;
 }
 private void addJobOutput(AbstractExecutable executable) throws PersistentException {
   ExecutableOutputPO executableOutputPO = new ExecutableOutputPO();
   executableOutputPO.setUuid(executable.getId());
   executableDao.addJobOutput(executableOutputPO);
   if (executable instanceof DefaultChainedExecutable) {
     for (AbstractExecutable subTask : ((DefaultChainedExecutable) executable).getTasks()) {
       addJobOutput(subTask);
     }
   }
 }
 // for migration only
 // TODO delete when migration finished
 public void resetJobOutput(String jobId, ExecutableState state, String output) {
   try {
     final ExecutableOutputPO jobOutput = executableDao.getJobOutput(jobId);
     jobOutput.setStatus(state.toString());
     if (output != null) {
       jobOutput.setContent(output);
     }
     executableDao.updateJobOutput(jobOutput);
   } catch (PersistentException e) {
     throw new RuntimeException(e);
   }
 }
 public Map<String, Output> getAllOutputs() {
   try {
     final List<ExecutableOutputPO> jobOutputs = executableDao.getJobOutputs();
     HashMap<String, Output> result = Maps.newHashMap();
     for (ExecutableOutputPO jobOutput : jobOutputs) {
       result.put(jobOutput.getId(), parseOutput(jobOutput));
     }
     return result;
   } catch (PersistentException e) {
     logger.error("fail to get all job output:", e);
     throw new RuntimeException(e);
   }
 }
 public void addJobInfo(String id, Map<String, String> info) {
   if (info == null) {
     return;
   }
   try {
     ExecutableOutputPO output = executableDao.getJobOutput(id);
     Preconditions.checkArgument(output != null, "there is no related output for job id:" + id);
     output.getInfo().putAll(info);
     executableDao.updateJobOutput(output);
   } catch (PersistentException e) {
     logger.error("error update job info, id:" + id + "  info:" + info.toString());
     throw new RuntimeException(e);
   }
 }
 public void updateAllRunningJobsToError() {
   try {
     final List<ExecutableOutputPO> jobOutputs = executableDao.getJobOutputs();
     for (ExecutableOutputPO executableOutputPO : jobOutputs) {
       if (executableOutputPO.getStatus().equalsIgnoreCase(ExecutableState.RUNNING.toString())) {
         executableOutputPO.setStatus(ExecutableState.ERROR.toString());
         executableDao.updateJobOutput(executableOutputPO);
       }
     }
   } catch (PersistentException e) {
     logger.error("error reset job status from RUNNING to ERROR", e);
     throw new RuntimeException(e);
   }
 }