Package com.google.appengine.tools.pipeline.impl.model

Source Code of com.google.appengine.tools.pipeline.impl.model.JobRecord

// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package com.google.appengine.tools.pipeline.impl.model;

import com.google.appengine.api.backends.BackendService;
import com.google.appengine.api.backends.BackendServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.modules.ModulesService;
import com.google.appengine.api.modules.ModulesServiceFactory;
import com.google.appengine.tools.pipeline.Job;
import com.google.appengine.tools.pipeline.JobInfo;
import com.google.appengine.tools.pipeline.JobSetting;
import com.google.appengine.tools.pipeline.JobSetting.BackoffFactor;
import com.google.appengine.tools.pipeline.JobSetting.BackoffSeconds;
import com.google.appengine.tools.pipeline.JobSetting.IntValuedSetting;
import com.google.appengine.tools.pipeline.JobSetting.MaxAttempts;
import com.google.appengine.tools.pipeline.JobSetting.OnBackend;
import com.google.appengine.tools.pipeline.JobSetting.OnModule;
import com.google.appengine.tools.pipeline.JobSetting.OnQueue;
import com.google.appengine.tools.pipeline.JobSetting.StatusConsoleUrl;
import com.google.appengine.tools.pipeline.JobSetting.WaitForSetting;
import com.google.appengine.tools.pipeline.impl.FutureValueImpl;
import com.google.appengine.tools.pipeline.impl.QueueSettings;
import com.google.appengine.tools.pipeline.impl.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* The Pipeline model object corresponding to a job.
*
* @author rudominer@google.com (Mitch Rudominer)
*/
public class JobRecord extends PipelineModelObject implements JobInfo {

  /**
   * The state of the job.
   */
  public static enum State {
    // TODO(user): document states (including valid transitions) and relation to JobInfo.State
    WAITING_TO_RUN, WAITING_TO_FINALIZE, FINALIZED, STOPPED, CANCELED, RETRY
  }

  public static final String EXCEPTION_HANDLER_METHOD_NAME = "handleException";

  /**
   * This enum serves as an input parameter to the method
   * {@link com.google.appengine.tools.pipeline.impl.backend.PipelineBackEnd#queryJob(
   * Key, InflationType)}. When fetching an
   * instance of {@code JobRecord} from the data store this enum specifies how
   * much auxiliary data should also be queried and used to inflate the instance
   * of {@code JobRecord}.
   *
   */
  public static enum InflationType {
    /**
     * Do not inflate at all
     */
    NONE,

    /**
     * Inflate as necessary to run the job. In particular:
     * <ul>
     * <li>{@link JobRecord#getRunBarrierInflated()} will not return
     * {@code null}; and
     * <li>for the returned {@link Barrier}
     * {@link Barrier#getWaitingOnInflated()} will not return {@code null}; and
     * <li> {@link JobRecord#getOutputSlotInflated()} will not return
     * {@code null}; and
     * <li> {@link JobRecord#getFinalizeBarrierInflated()} will not return
     * {@code null}
     * </ul>
     */
    FOR_RUN,

    /**
     * Inflate as necessary to finalize the job. In particular:
     * <ul>
     * <li> {@link JobRecord#getOutputSlotInflated()} will not return
     * {@code null}; and
     * <li> {@link JobRecord#getFinalizeBarrierInflated()} will not return
     * {@code null}; and
     * <li>for the returned {@link Barrier} the method
     * {@link Barrier#getWaitingOnInflated()} will not return {@code null}.
     * </ul>
     */
    FOR_FINALIZE,

    /**
     * Inflate as necessary to retrieve the output of the job. In particular
     * {@link JobRecord#getOutputSlotInflated()} will not return {@code null}
     */
    FOR_OUTPUT;
  }

  public static final String DATA_STORE_KIND = "pipeline-job";
  // Data store entity property names
  private static final String JOB_INSTANCE_PROPERTY = "jobInstance";
  private static final String RUN_BARRIER_PROPERTY = "runBarrier";
  private static final String FINALIZE_BARRIER_PROPERTY = "finalizeBarrier";
  private static final String STATE_PROPERTY = "state";
  private static final String EXCEPTION_HANDLING_ANCESTOR_KEY_PROPERTY =
      "exceptionHandlingAncestorKey";
  private static final String EXCEPTION_HANDLER_SPECIFIED_PROPERTY = "hasExceptionHandler";
  private static final String EXCEPTION_HANDLER_JOB_KEY_PROPERTY = "exceptionHandlerJobKey";
  private static final String EXCEPTION_HANDLER_JOB_GRAPH_GUID_PROPERTY =
      "exceptionHandlerJobGraphGuid";
  private static final String CALL_EXCEPTION_HANDLER_PROPERTY = "callExceptionHandler";
  private static final String IGNORE_EXCEPTION_PROPERTY = "ignoreException";
  private static final String OUTPUT_SLOT_PROPERTY = "outputSlot";
  private static final String EXCEPTION_KEY_PROPERTY = "exceptionKey";
  private static final String START_TIME_PROPERTY = "startTime";
  private static final String END_TIME_PROPERTY = "endTime";
  private static final String CHILD_KEYS_PROPERTY = "childKeys";
  private static final String ATTEMPT_NUM_PROPERTY = "attemptNum";
  private static final String MAX_ATTEMPTS_PROPERTY = "maxAttempts";
  private static final String BACKOFF_SECONDS_PROPERTY = "backoffSeconds";
  private static final String BACKOFF_FACTOR_PROPERTY = "backoffFactor";
  private static final String ON_BACKEND_PROPERTY = "onBackend";
  private static final String ON_MODULE_PROPERTY = "onModule";
  private static final String ON_QUEUE_PROPERTY = "onQueue";
  private static final String MODULE_VERSION_PROPERTY = "moduleVersion";
  private static final String CHILD_GRAPH_GUID_PROPERTY = "childGraphGuid";
  private static final String STATUS_CONSOLE_URL = "statusConsoleUrl";
  public static final String ROOT_JOB_DISPLAY_NAME = "rootJobDisplayName";

  // persistent fields
  private final Key jobInstanceKey;
  private final Key runBarrierKey;
  private final Key finalizeBarrierKey;
  private Key outputSlotKey;
  private State state;
  private Key exceptionHandlingAncestorKey;
  private boolean exceptionHandlerSpecified;
  private Key exceptionHandlerJobKey;
  private String exceptionHandlerJobGraphGuid;
  private boolean callExceptionHandler;
  private boolean ignoreException;
  private Key exceptionKey;
  private Date startTime;
  private Date endTime;
  private String childGraphGuid;
  private List<Key> childKeys;
  private long attemptNumber;
  private long maxAttempts = JobSetting.MaxAttempts.DEFAULT;
  private long backoffSeconds = JobSetting.BackoffSeconds.DEFAULT;
  private long backoffFactor = JobSetting.BackoffFactor.DEFAULT;
  private final QueueSettings queueSettings = new QueueSettings();
  private String statusConsoleUrl;
  private String rootJobDisplayName;

  // transient fields
  private Barrier runBarrierInflated;
  private Barrier finalizeBarrierInflated;
  private Slot outputSlotInflated;
  private JobInstanceRecord jobInstanceRecordInflated;
  private Throwable exceptionInflated;

  /**
   * Re-constitutes an instance of this class from a Data Store entity.
   *
   * @param entity
   */
  public JobRecord(Entity entity) {
    super(entity);
    jobInstanceKey = (Key) entity.getProperty(JOB_INSTANCE_PROPERTY);
    finalizeBarrierKey = (Key) entity.getProperty(FINALIZE_BARRIER_PROPERTY);
    runBarrierKey = (Key) entity.getProperty(RUN_BARRIER_PROPERTY);
    outputSlotKey = (Key) entity.getProperty(OUTPUT_SLOT_PROPERTY);
    state = State.valueOf((String) entity.getProperty(STATE_PROPERTY));
    exceptionHandlingAncestorKey =
        (Key) entity.getProperty(EXCEPTION_HANDLING_ANCESTOR_KEY_PROPERTY);
    Object exceptionHandlerSpecifiedProperty =
        entity.getProperty(EXCEPTION_HANDLER_SPECIFIED_PROPERTY);
    if (null != exceptionHandlerSpecifiedProperty) {
      exceptionHandlerSpecified = (Boolean) exceptionHandlerSpecifiedProperty;
    }
    exceptionHandlerJobKey = (Key) entity.getProperty(EXCEPTION_HANDLER_JOB_KEY_PROPERTY);
    Text exceptionHandlerGraphGuidText =
        (Text) entity.getProperty(EXCEPTION_HANDLER_JOB_GRAPH_GUID_PROPERTY);
    if (null != exceptionHandlerGraphGuidText) {
      exceptionHandlerJobGraphGuid = exceptionHandlerGraphGuidText.getValue();
    }
    Object callExceptionHandlerProperty = entity.getProperty(CALL_EXCEPTION_HANDLER_PROPERTY);
    if (null != callExceptionHandlerProperty) {
      callExceptionHandler = (Boolean) callExceptionHandlerProperty;
    }
    Object ignoreExceptionProperty = entity.getProperty(IGNORE_EXCEPTION_PROPERTY);
    if (null != ignoreExceptionProperty) {
      ignoreException = (Boolean) ignoreExceptionProperty;
    }
    Text childGraphGuidText = (Text) entity.getProperty(CHILD_GRAPH_GUID_PROPERTY);
    if (null != childGraphGuidText) {
      childGraphGuid = childGraphGuidText.getValue();
    }
    exceptionKey = (Key) entity.getProperty(EXCEPTION_KEY_PROPERTY);
    startTime = (Date) entity.getProperty(START_TIME_PROPERTY);
    endTime = (Date) entity.getProperty(END_TIME_PROPERTY);
    childKeys = (List<Key>) entity.getProperty(CHILD_KEYS_PROPERTY);
    if (null == childKeys) {
      childKeys = new LinkedList<>();
    }
    attemptNumber = (Long) entity.getProperty(ATTEMPT_NUM_PROPERTY);
    maxAttempts = (Long) entity.getProperty(MAX_ATTEMPTS_PROPERTY);
    backoffSeconds = (Long) entity.getProperty(BACKOFF_SECONDS_PROPERTY);
    backoffFactor = (Long) entity.getProperty(BACKOFF_FACTOR_PROPERTY);
    queueSettings.setOnBackend((String) entity.getProperty(ON_BACKEND_PROPERTY));
    queueSettings.setOnModule((String) entity.getProperty(ON_MODULE_PROPERTY));
    queueSettings.setModuleVersion((String) entity.getProperty(MODULE_VERSION_PROPERTY));
    queueSettings.setOnQueue((String) entity.getProperty(ON_QUEUE_PROPERTY));
    statusConsoleUrl = (String) entity.getProperty(STATUS_CONSOLE_URL);
    rootJobDisplayName = (String) entity.getProperty(ROOT_JOB_DISPLAY_NAME);
  }

  /**
   * Constructs and returns a Data Store Entity that represents this model
   * object
   */
  @Override
  public Entity toEntity() {
    Entity entity = toProtoEntity();
    entity.setProperty(JOB_INSTANCE_PROPERTY, jobInstanceKey);
    entity.setProperty(FINALIZE_BARRIER_PROPERTY, finalizeBarrierKey);
    entity.setProperty(RUN_BARRIER_PROPERTY, runBarrierKey);
    entity.setProperty(OUTPUT_SLOT_PROPERTY, outputSlotKey);
    entity.setProperty(STATE_PROPERTY, state.toString());
    if (null != exceptionHandlingAncestorKey) {
      entity.setProperty(EXCEPTION_HANDLING_ANCESTOR_KEY_PROPERTY, exceptionHandlingAncestorKey);
    }
    if (exceptionHandlerSpecified) {
      entity.setProperty(EXCEPTION_HANDLER_SPECIFIED_PROPERTY, Boolean.TRUE);
    }
    if (null != exceptionHandlerJobKey) {
      entity.setProperty(EXCEPTION_HANDLER_JOB_KEY_PROPERTY, exceptionHandlerJobKey);
    }
    if (null != exceptionKey) {
      entity.setProperty(EXCEPTION_KEY_PROPERTY, exceptionKey);
    }
    if (null != exceptionHandlerJobGraphGuid) {
      entity.setUnindexedProperty(
          EXCEPTION_HANDLER_JOB_GRAPH_GUID_PROPERTY, new Text(exceptionHandlerJobGraphGuid));
    }
    entity.setUnindexedProperty(CALL_EXCEPTION_HANDLER_PROPERTY, callExceptionHandler);
    entity.setUnindexedProperty(IGNORE_EXCEPTION_PROPERTY, ignoreException);
    if (childGraphGuid != null) {
      entity.setUnindexedProperty(CHILD_GRAPH_GUID_PROPERTY, new Text(childGraphGuid));
    }
    entity.setProperty(START_TIME_PROPERTY, startTime);
    entity.setUnindexedProperty(END_TIME_PROPERTY, endTime);
    entity.setProperty(CHILD_KEYS_PROPERTY, childKeys);
    entity.setUnindexedProperty(ATTEMPT_NUM_PROPERTY, attemptNumber);
    entity.setUnindexedProperty(MAX_ATTEMPTS_PROPERTY, maxAttempts);
    entity.setUnindexedProperty(BACKOFF_SECONDS_PROPERTY, backoffSeconds);
    entity.setUnindexedProperty(BACKOFF_FACTOR_PROPERTY, backoffFactor);
    entity.setUnindexedProperty(ON_BACKEND_PROPERTY, queueSettings.getOnBackend());
    entity.setUnindexedProperty(ON_MODULE_PROPERTY, queueSettings.getOnModule());
    entity.setUnindexedProperty(MODULE_VERSION_PROPERTY, queueSettings.getModuleVersion());
    entity.setUnindexedProperty(ON_QUEUE_PROPERTY, queueSettings.getOnQueue());
    entity.setUnindexedProperty(STATUS_CONSOLE_URL, statusConsoleUrl);
    if (rootJobDisplayName != null) {
      entity.setProperty(ROOT_JOB_DISPLAY_NAME, rootJobDisplayName);
    }
    return entity;
  }

  /**
   * Constructs a new JobRecord given the provided data. The constructed
   * instance will be inflated in the sense that
   * {@link #getJobInstanceInflated()}, {@link #getFinalizeBarrierInflated()},
   * {@link #getOutputSlotInflated()} and {@link #getRunBarrierInflated()} will
   * all not return {@code null}. This constructor is used when a new JobRecord
   * is created during the run() method of a parent job. The parent job is also
   * known as the generator job.
   *
   * @param generatorJob The parent generator job of this job.
   * @param graphGUIDParam The GUID of the local graph of this job.
   * @param jobInstance The non-null user-supplied instance of {@code Job} that
   *        implements the Job that the newly created JobRecord represents.
   * @param callExceptionHandler The flag that indicates that this job should call
   *        {@code Job#handleException(Throwable)} instead of {@code run}.
   * @param settings Array of {@code JobSetting} to apply to the newly created
   *        JobRecord.
   */
  public JobRecord(JobRecord generatorJob, String graphGUIDParam, Job<?> jobInstance,
      boolean callExceptionHandler, JobSetting[] settings) {
    this(generatorJob.getRootJobKey(), null, generatorJob.getKey(), graphGUIDParam, jobInstance,
        callExceptionHandler, settings, generatorJob.getQueueSettings());
    // If generator job has exception handler then it should be called in case
    // of this job throwing to create an exception handling child job.
    // If callExceptionHandler is true then this job is an exception handling
    // child and its exceptions should be handled by its parent's
    // exceptionHandlingAncestor to avoid infinite recursion.
    if (generatorJob.isExceptionHandlerSpecified() && !callExceptionHandler) {
      exceptionHandlingAncestorKey = generatorJob.getKey();
    } else {
      exceptionHandlingAncestorKey = generatorJob.getExceptionHandlingAncestorKey();
    }
    // Inherit settings from generator job
    Map<Class<? extends JobSetting>, JobSetting> settingsMap = new HashMap<>();
    for (JobSetting setting : settings) {
      settingsMap.put(setting.getClass(), setting);
    }
    if (!settingsMap.containsKey(StatusConsoleUrl.class)) {
      statusConsoleUrl = generatorJob.statusConsoleUrl;
    }
  }

  private JobRecord(Key rootJobKey, Key thisKey, Key generatorJobKey, String graphGUID,
      Job<?> jobInstance, boolean callExceptionHandler, JobSetting[] settings,
      QueueSettings parentQueueSettings) {
    super(rootJobKey, null, thisKey, generatorJobKey, graphGUID);
    jobInstanceRecordInflated = new JobInstanceRecord(this, jobInstance);
    jobInstanceKey = jobInstanceRecordInflated.getKey();
    exceptionHandlerSpecified = isExceptionHandlerSpecified(jobInstance);
    this.callExceptionHandler = callExceptionHandler;
    runBarrierInflated = new Barrier(Barrier.Type.RUN, this);
    runBarrierKey = runBarrierInflated.getKey();
    finalizeBarrierInflated = new Barrier(Barrier.Type.FINALIZE, this);
    finalizeBarrierKey = finalizeBarrierInflated.getKey();
    outputSlotInflated = new Slot(getRootJobKey(), getGeneratorJobKey(), getGraphGuid());
    // Initially we set the filler of the output slot to be this Job.
    // During finalize we may reset it to the filler of the finalize slot.
    outputSlotInflated.setSourceJobKey(getKey());
    outputSlotKey = outputSlotInflated.getKey();
    childKeys = new LinkedList<>();
    state = State.WAITING_TO_RUN;
    for (JobSetting setting : settings) {
      applySetting(setting);
    }
    if (parentQueueSettings != null) {
      queueSettings.merge(parentQueueSettings);
    }
    if (queueSettings.getOnBackend() == null) {
      String module = queueSettings.getOnModule();
      if (module == null) {
        BackendService backendSerivce = BackendServiceFactory.getBackendService();
        String currentBackend = backendSerivce.getCurrentBackend();
        // If currentBackend contains ':' it is actually a B type module (see b/12893879)
        if (currentBackend != null && currentBackend.indexOf(':') == -1) {
          queueSettings.setOnBackend(currentBackend);
        } else {
          ModulesService modulesService = ModulesServiceFactory.getModulesService();
          queueSettings.setOnModule(modulesService.getCurrentModule());
          queueSettings.setModuleVersion(modulesService.getCurrentVersion());
        }
      } else {
        ModulesService modulesService = ModulesServiceFactory.getModulesService();
        if (module.equals(modulesService.getCurrentModule())) {
          queueSettings.setModuleVersion(modulesService.getCurrentVersion());
        } else {
          queueSettings.setModuleVersion(modulesService.getDefaultVersion(module));
        }
      }
    }
  }

  // Constructor for Root Jobs (called by {@link #createRootJobRecord}).
  private JobRecord(Key key, Job<?> jobInstance, JobSetting[] settings) {
    // Root Jobs have their rootJobKey the same as their keys and provide null for generatorKey
    // and graphGUID. Also, callExceptionHandler is always false.
    this(key, key, null, null, jobInstance, false, settings, null);
    rootJobDisplayName = jobInstance.getJobDisplayName();
  }

  /**
   * A factory method for root jobs.
   *
   * @param jobInstance The non-null user-supplied instance of {@code Job} that
   *        implements the Job that the newly created JobRecord represents.
   * @param settings Array of {@code JobSetting} to apply to the newly created
   *        JobRecord.
   */
  public static JobRecord createRootJobRecord(Job<?> jobInstance, JobSetting[] settings) {
    Key key = generateKey(null, DATA_STORE_KIND);
    return new JobRecord(key, jobInstance, settings);
  }

  public static boolean isExceptionHandlerSpecified(Job<?> jobInstance) {
    boolean result = false;
    Class<?> clazz = jobInstance.getClass();
    for (Method method : clazz.getMethods()) {
      if (method.getName().equals(EXCEPTION_HANDLER_METHOD_NAME)) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length != 1 || !Throwable.class.isAssignableFrom(parameterTypes[0])) {
          throw new RuntimeException(method
              + " has invalid signature. It must have exactly one paramter of type "
              + "Throwable or any of its descendants");
        }
        result = true;
        // continue looping to check signature of all handleException methods
      }
    }
    return result;
  }

  private void applySetting(JobSetting setting) {
    if (setting instanceof WaitForSetting) {
      WaitForSetting wf = (WaitForSetting) setting;
      FutureValueImpl<?> fv = (FutureValueImpl<?>) wf.getValue();
      Slot slot = fv.getSlot();
      runBarrierInflated.addPhantomArgumentSlot(slot);
    } else if (setting instanceof IntValuedSetting) {
      int value = ((IntValuedSetting) setting).getValue();
      if (setting instanceof BackoffSeconds) {
        backoffSeconds = value;
      } else if (setting instanceof BackoffFactor) {
        backoffFactor = value;
      } else if (setting instanceof MaxAttempts) {
        maxAttempts = value;
      } else {
        throw new RuntimeException("Unrecognized JobOption class " + setting.getClass().getName());
      }
    } else if (setting instanceof OnBackend) {
      queueSettings.setOnBackend(((OnBackend) setting).getValue());
    } else if (setting instanceof OnModule) {
      queueSettings.setOnModule(((OnModule) setting).getValue());
    } else if (setting instanceof OnQueue) {
      queueSettings.setOnQueue(((OnQueue) setting).getValue());
    } else if (setting instanceof StatusConsoleUrl){
      statusConsoleUrl = ((StatusConsoleUrl) setting).getValue();
    } else {
      throw new RuntimeException("Unrecognized JobOption class " + setting.getClass().getName());
    }
  }

  @Override
  protected String getDatastoreKind() {
    return DATA_STORE_KIND;
  }

  private static boolean checkForInflate(PipelineModelObject obj, Key expectedGuid, String name) {
    if (null == obj) {
      return false;
    }
    if (!expectedGuid.equals(obj.getKey())) {
      throw new IllegalArgumentException(
          "Wrong guid for " + name + ". Expected " + expectedGuid + " but was " + obj.getKey());
    }
    return true;
  }

  public void inflate(Barrier runBarrier, Barrier finalizeBarrier, Slot outputSlot,
      JobInstanceRecord jobInstanceRecord, ExceptionRecord exceptionRecord) {
    if (checkForInflate(runBarrier, runBarrierKey, "runBarrier")) {
      runBarrierInflated = runBarrier;
    }
    if (checkForInflate(finalizeBarrier, finalizeBarrierKey, "finalizeBarrier")) {
      finalizeBarrierInflated = finalizeBarrier;
    }
    if (checkForInflate(outputSlot, outputSlotKey, "outputSlot")) {
      outputSlotInflated = outputSlot;
    }
    if (checkForInflate(jobInstanceRecord, jobInstanceKey, "jobInstanceRecord")) {
      jobInstanceRecordInflated = jobInstanceRecord;
    }
    if (checkForInflate(exceptionRecord, exceptionKey, "exception")) {
      exceptionInflated = exceptionRecord.getException();
    }
  }

  public Key getRunBarrierKey() {
    return runBarrierKey;
  }

  public Barrier getRunBarrierInflated() {
    return runBarrierInflated;
  }

  public Key getFinalizeBarrierKey() {
    return finalizeBarrierKey;
  }

  public Barrier getFinalizeBarrierInflated() {
    return finalizeBarrierInflated;
  }

  public Key getOutputSlotKey() {
    return outputSlotKey;
  }

  /**
   * Used to set exceptionHandling Job output to the same slot as the protected job.
   */
  public void setOutputSlotInflated(Slot outputSlot) {
    outputSlotInflated = outputSlot;
    outputSlotKey = outputSlot.getKey();
  }

  public Slot getOutputSlotInflated() {
    return outputSlotInflated;
  }

  public Key getJobInstanceKey() {
    return jobInstanceKey;
  }

  public JobInstanceRecord getJobInstanceInflated() {
    return jobInstanceRecordInflated;
  }

  public void setStartTime(Date date) {
    startTime = date;
  }

  public Date getStartTime() {
    return startTime;
  }

  public void setEndTime(Date date) {
    endTime = date;
  }

  public Date getEndTime() {
    return endTime;
  }

  public void setState(State state) {
    this.state = state;
  }

  public void setChildGraphGuid(String guid) {
    childGraphGuid = guid;
  }

  public State getState() {
    return state;
  }

  public boolean isExceptionHandlerSpecified() {
    // If this job is exception handler itself then it has exceptionHandlerSpecified
    // but it shouldn't delegate to it.
    return exceptionHandlerSpecified && (!isCallExceptionHandler());
  }

  /**
   * Returns key of the nearest ancestor that has exceptionHandler method
   * overridden or <code>null</code> if none of them has it.
   */
  public Key getExceptionHandlingAncestorKey() {
    return exceptionHandlingAncestorKey;
  }

  public Key getExceptionHandlerJobKey() {
    return exceptionHandlerJobKey;
  }

  public String getExceptionHandlerJobGraphGuid() {
    return exceptionHandlerJobGraphGuid;
  }

  public void setExceptionHandlerJobGraphGuid(String exceptionHandlerJobGraphGuid) {
    this.exceptionHandlerJobGraphGuid = exceptionHandlerJobGraphGuid;
  }

  /**
   * If true then this job is exception handler and
   * {@code Job#handleException(Throwable)} should be called instead of <code>run
   * </code>.
   */
  public boolean isCallExceptionHandler() {
    return callExceptionHandler;
  }

  /**
   * If <code>true</code> then an exception during a job execution is ignored. It is
   * expected to be set to <code>true</code> for jobs that execute error handler due
   * to cancellation.
   */
  public boolean isIgnoreException() {
    return ignoreException;
  }

  public void setIgnoreException(boolean ignoreException) {
    this.ignoreException = ignoreException;
  }

  public int getAttemptNumber() {
    return (int) attemptNumber;
  }

  public void incrementAttemptNumber() {
    attemptNumber++;
  }

  public int getBackoffSeconds() {
    return (int) backoffSeconds;
  }

  public int getBackoffFactor() {
    return (int) backoffFactor;
  }

  public int getMaxAttempts() {
    return (int) maxAttempts;
  }

  /**
   * Returns a copy of QueueSettings
   */
  public QueueSettings getQueueSettings() {
    return queueSettings;
  }

  public String getStatusConsoleUrl() {
    return statusConsoleUrl;
  }

  public void setStatusConsoleUrl(String statusConsoleUrl) {
    this.statusConsoleUrl = statusConsoleUrl;
  }

  public void appendChildKey(Key key) {
    childKeys.add(key);
  }

  public List<Key> getChildKeys() {
    return childKeys;
  }

  public String getChildGraphGuid() {
    return childGraphGuid;
  }

  public void setExceptionKey(Key exceptionKey) {
    this.exceptionKey = exceptionKey;
  }

  @Override
  public JobInfo.State getJobState() {
    switch (state) {
      case WAITING_TO_RUN:
      case WAITING_TO_FINALIZE:
        return JobInfo.State.RUNNING;
      case FINALIZED:
        return JobInfo.State.COMPLETED_SUCCESSFULLY;
      case CANCELED:
        return JobInfo.State.CANCELED_BY_REQUEST;
      case STOPPED:
        if (null == exceptionKey) {
          return JobInfo.State.STOPPED_BY_REQUEST;
        } else {
          return JobInfo.State.STOPPED_BY_ERROR;
        }
      case RETRY:
        return JobInfo.State.WAITING_TO_RETRY;
      default:
        throw new RuntimeException("Unrecognized state: " + state);
    }
  }

  @Override
  public Object getOutput() {
    if (null == outputSlotInflated) {
      return null;
    } else {
      return outputSlotInflated.getValue();
    }
  }

  @Override
  public String getError() {
    if (exceptionInflated == null) {
      return null;
    }
    return StringUtils.printStackTraceToString(exceptionInflated);
  }

  @Override
  public Throwable getException() {
    return exceptionInflated;
  }

  public Key getExceptionKey() {
    return exceptionKey;
  }

  public String getRootJobDisplayName() {
    return rootJobDisplayName;
  }

  private String getJobInstanceString() {
    if (null == jobInstanceRecordInflated) {
      return "jobInstanceKey=" + jobInstanceKey;
    }
    String jobClass = jobInstanceRecordInflated.getClassName();
    return jobClass + (callExceptionHandler ? ".handleException" : ".run");
  }

  @Override
  public String toString() {
    return "JobRecord [" + getKeyName(getKey()) + ", " + state + ", " + getJobInstanceString()
        + ", callExceptionJHandler=" + callExceptionHandler + ", runBarrier="
        + runBarrierKey.getName() + ", finalizeBarrier=" + finalizeBarrierKey.getName()
        + ", outputSlot=" + outputSlotKey.getName() + ", rootJobDisplayName="
        + rootJobDisplayName + ", parent=" + getKeyName(getGeneratorJobKey()) + ", guid="
        + getGraphGuid() + ", childGuid=" + childGraphGuid + "]";
  }
}
TOP

Related Classes of com.google.appengine.tools.pipeline.impl.model.JobRecord

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.