// Effective time will be right after the last running instance.
 private String getEffectiveTime(
     Entity entity, String cluster, Collection<InstanceState> instances) throws FalconException {
   if (instances == null || instances.isEmpty()) {
     return SchemaHelper.formatDateUTC(DateUtil.now());
   } else {
     List<InstanceState> instanceList = new ArrayList(instances);
     Collections.sort(
         instanceList,
         new Comparator<InstanceState>() {
           @Override
           public int compare(InstanceState x, InstanceState y) {
             return (x.getInstance().getInstanceSequence() < y.getInstance().getInstanceSequence())
                 ? -1
                 : (x.getInstance().getInstanceSequence() == y.getInstance().getInstanceSequence()
                     ? 0
                     : 1);
           }
         });
     // Get the last element as the list is sorted in ascending order
     Date lastRunningInstanceTime =
         instanceList.get(instanceList.size() - 1).getInstance().getInstanceTime().toDate();
     Cluster clusterEntity = ConfigurationStore.get().get(EntityType.CLUSTER, cluster);
     // Offset the time by a few seconds, else nextStartTime will be same as the reference time.
     Date effectiveTime =
         EntityUtil.getNextStartTime(
             entity, clusterEntity, DateUtil.offsetTime(lastRunningInstanceTime, 10));
     return SchemaHelper.formatDateUTC(effectiveTime);
   }
 }
 @Override
 public String touch(Entity entity, String cluster, Boolean skipDryRun) throws FalconException {
   EntityID id = new EntityID(entity);
   // Ideally state store should have all entities, but, check anyway.
   if (STATE_STORE.entityExists(id)) {
     Date endTime = EntityUtil.getEndTime(entity, cluster);
     if (endTime.before(DateUtil.now())) {
       throw new FalconException(
           "Entity's end time "
               + SchemaHelper.formatDateUTC(endTime)
               + " is before current time. Entity can't be touch-ed as it has completed.");
     }
     Collection<InstanceState> instances =
         STATE_STORE.getExecutionInstances(entity, cluster, InstanceState.getRunningStates());
     // touch should happen irrespective of the state the entity is in.
     DAGEngineFactory.getDAGEngine(cluster)
         .touch(entity, (skipDryRun == null) ? Boolean.FALSE : skipDryRun);
     StringBuilder builder = new StringBuilder();
     builder
         .append(entity.toShortString())
         .append("/Effective Time: ")
         .append(getEffectiveTime(entity, cluster, instances));
     return builder.toString();
   }
   throw new FalconException("Could not find entity " + id + " in state store.");
 }
  @Override
  public String update(Entity oldEntity, Entity newEntity, String cluster, Boolean skipDryRun)
      throws FalconException {
    org.apache.falcon.entity.v0.cluster.Cluster clusterEntity =
        ConfigurationStore.get().get(EntityType.CLUSTER, cluster);
    boolean entityUpdated =
        UpdateHelper.isEntityUpdated(
            oldEntity,
            newEntity,
            cluster,
            EntityUtil.getLatestStagingPath(clusterEntity, oldEntity));
    StringBuilder result = new StringBuilder();
    if (!entityUpdated) {
      // Ideally should throw an exception, but, keeping it backward-compatible.
      LOG.warn(
          "No relevant updates detected in the new entity definition for entity {}!",
          newEntity.getName());
      return result.toString();
    }

    Date oldEndTime = EntityUtil.getEndTime(oldEntity, cluster);
    Date newEndTime = EntityUtil.getEndTime(newEntity, cluster);
    if (newEndTime.before(DateUtil.now()) || newEndTime.before(oldEndTime)) {
      throw new FalconException(
          "New Entity's end time "
              + SchemaHelper.formatDateUTC(newEndTime)
              + " is before current time or before old end time. Entity can't be updated.");
    }

    // The steps required are the same as touch.
    DAGEngineFactory.getDAGEngine(cluster)
        .touch(newEntity, (skipDryRun == null) ? Boolean.FALSE : skipDryRun);
    // Additionally, update the executor.
    // The update will kick in for new instances created and not for READY/WAITING instances, as
    // with Oozie.
    Collection<InstanceState> instances = new ArrayList<>();
    instances.add(STATE_STORE.getLastExecutionInstance(oldEntity, cluster));
    EXECUTION_SERVICE.getEntityExecutor(oldEntity, cluster).update(newEntity);

    result
        .append(newEntity.toShortString())
        .append("/Effective Time: ")
        .append(getEffectiveTime(newEntity, cluster, instances));
    return result.toString();
  }