Package eu.stratosphere.nephele.executiongraph

Source Code of eu.stratosphere.nephele.executiongraph.ExecutionVertex

/***********************************************************************************************************************
* Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu)
*
* 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 eu.stratosphere.nephele.executiongraph;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import eu.stratosphere.nephele.taskmanager.TaskKillResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import eu.stratosphere.nephele.deployment.ChannelDeploymentDescriptor;
import eu.stratosphere.nephele.deployment.GateDeploymentDescriptor;
import eu.stratosphere.nephele.deployment.TaskDeploymentDescriptor;
import eu.stratosphere.nephele.execution.ExecutionListener;
import eu.stratosphere.nephele.execution.ExecutionState;
import eu.stratosphere.nephele.execution.ExecutionStateTransition;
import eu.stratosphere.nephele.instance.AllocatedResource;
import eu.stratosphere.nephele.instance.AllocationID;
import eu.stratosphere.runtime.io.gates.GateID;
import eu.stratosphere.nephele.taskmanager.AbstractTaskResult;
import eu.stratosphere.nephele.taskmanager.AbstractTaskResult.ReturnCode;
import eu.stratosphere.nephele.taskmanager.TaskCancelResult;
import eu.stratosphere.nephele.taskmanager.TaskSubmissionResult;
import eu.stratosphere.nephele.util.AtomicEnum;
import eu.stratosphere.nephele.util.SerializableArrayList;
import eu.stratosphere.util.StringUtils;

/**
* An execution vertex represents an instance of a task in a Nephele job. An execution vertex
* is initially created from a job vertex and always belongs to exactly one group vertex.
* It is possible to duplicate execution vertices in order to distribute a task to several different
* task managers and process the task in parallel.
* <p>
* This class is thread-safe.
*
*/
public final class ExecutionVertex {

  /**
   * The log object used for debugging.
   */
  private static final Log LOG = LogFactory.getLog(ExecutionVertex.class);

  /**
   * The ID of the vertex.
   */
  private final ExecutionVertexID vertexID;

  /**
   * The group vertex this vertex belongs to.
   */
  private final ExecutionGroupVertex groupVertex;

  /**
   * The execution graph is vertex belongs to.
   */
  private final ExecutionGraph executionGraph;

  /**
   * The allocated resources assigned to this vertex.
   */
  private final AtomicReference<AllocatedResource> allocatedResource = new AtomicReference<AllocatedResource>(null);

  /**
   * The allocation ID identifying the allocated resources used by this vertex
   * within the instance.
   */
  private volatile AllocationID allocationID = null;

  /**
   * A list of {@link VertexAssignmentListener} objects to be notified about changes in the instance assignment.
   */
  private final CopyOnWriteArrayList<VertexAssignmentListener> vertexAssignmentListeners = new CopyOnWriteArrayList<VertexAssignmentListener>();

  /**
   * A map of {@link ExecutionListener} objects to be notified about the state changes of a vertex.
   */
  private final ConcurrentMap<Integer, ExecutionListener> executionListeners = new ConcurrentSkipListMap<Integer, ExecutionListener>();

  /**
   * The current execution state of the task represented by this vertex
   */
  private final AtomicEnum<ExecutionState> executionState = new AtomicEnum<ExecutionState>(ExecutionState.CREATED);

  /**
   * The output gates attached to this vertex.
   */
  private final ExecutionGate[] outputGates;

  /**
   * The input gates attached to his vertex.
   */
  private final ExecutionGate[] inputGates;

  /**
   * The index of this vertex in the vertex group.
   */
  private volatile int indexInVertexGroup = 0;

  /**
   * Stores the number of times the vertex may be still be started before the corresponding task is considered to be
   * failed.
   */
  private final AtomicInteger retriesLeft;


  /**
   * The execution pipeline this vertex is part of.
   */
  private final AtomicReference<ExecutionPipeline> executionPipeline = new AtomicReference<ExecutionPipeline>(null);

  /**
   * Flag to indicate whether the vertex has been requested to cancel while in state STARTING
   */
  private final AtomicBoolean cancelRequested = new AtomicBoolean(false);

  /**
   * Create a new execution vertex and instantiates its environment.
   *
   * @param executionGraph
   *        the execution graph the new vertex belongs to
   * @param groupVertex
   *        the group vertex the new vertex belongs to
   * @param numberOfOutputGates
   *        the number of output gates attached to this vertex
   * @param numberOfInputGates
   *        the number of input gates attached to this vertex
   */
  public ExecutionVertex(final ExecutionGraph executionGraph, final ExecutionGroupVertex groupVertex,
      final int numberOfOutputGates, final int numberOfInputGates) {
    this(new ExecutionVertexID(), executionGraph, groupVertex, numberOfOutputGates, numberOfInputGates);

    this.groupVertex.addInitialSubtask(this);
  }

  /**
   * Private constructor used to duplicate execution vertices.
   *
   * @param vertexID
   *        the ID of the new execution vertex.
   * @param executionGraph
   *        the execution graph the new vertex belongs to
   * @param groupVertex
   *        the group vertex the new vertex belongs to
   * @param numberOfOutputGates
   *        the number of output gates attached to this vertex
   * @param numberOfInputGates
   *        the number of input gates attached to this vertex
   */
  private ExecutionVertex(final ExecutionVertexID vertexID, final ExecutionGraph executionGraph,
      final ExecutionGroupVertex groupVertex, final int numberOfOutputGates, final int numberOfInputGates) {

    this.vertexID = vertexID;
    this.executionGraph = executionGraph;
    this.groupVertex = groupVertex;

    this.retriesLeft = new AtomicInteger(groupVertex.getNumberOfExecutionRetries());

    this.outputGates = new ExecutionGate[numberOfOutputGates];
    this.inputGates = new ExecutionGate[numberOfInputGates];

    // Register vertex with execution graph
    this.executionGraph.registerExecutionVertex(this);

    // Register the vertex itself as a listener for state changes
    registerExecutionListener(this.executionGraph);
  }

  /**
   * Returns the group vertex this execution vertex belongs to.
   *
   * @return the group vertex this execution vertex belongs to
   */
  public ExecutionGroupVertex getGroupVertex() {
    return this.groupVertex;
  }

  /**
   * Returns the name of the execution vertex.
   *
   * @return the name of the execution vertex
   */
  public String getName() {
    return this.groupVertex.getName();
  }

  /**
   * Returns a duplicate of this execution vertex.
   *
   * @param preserveVertexID
   *        <code>true</code> to copy the vertex's ID to the duplicated vertex, <code>false</code> to create a new ID
   * @return a duplicate of this execution vertex
   */
  public ExecutionVertex duplicateVertex(final boolean preserveVertexID) {

    ExecutionVertexID newVertexID;
    if (preserveVertexID) {
      newVertexID = this.vertexID;
    } else {
      newVertexID = new ExecutionVertexID();
    }

    final ExecutionVertex duplicatedVertex = new ExecutionVertex(newVertexID, this.executionGraph,
      this.groupVertex, this.outputGates.length, this.inputGates.length);

    // Duplicate gates
    for (int i = 0; i < this.outputGates.length; ++i) {
      duplicatedVertex.outputGates[i] = new ExecutionGate(new GateID(), duplicatedVertex,
        this.outputGates[i].getGroupEdge(), false);
    }

    for (int i = 0; i < this.inputGates.length; ++i) {
      duplicatedVertex.inputGates[i] = new ExecutionGate(new GateID(), duplicatedVertex,
        this.inputGates[i].getGroupEdge(), true);
    }

    // TODO set new profiling record with new vertex id
    duplicatedVertex.setAllocatedResource(this.allocatedResource.get());

    return duplicatedVertex;
  }

  /**
   * Inserts the output gate at the given position.
   *
   * @param pos
   *        the position to insert the output gate
   * @param outputGate
   *        the output gate to be inserted
   */
  void insertOutputGate(final int pos, final ExecutionGate outputGate) {

    if (this.outputGates[pos] != null) {
      throw new IllegalStateException("Output gate at position " + pos + " is not null");
    }

    this.outputGates[pos] = outputGate;
  }

  /**
   * Inserts the input gate at the given position.
   *
   * @param pos
   *        the position to insert the input gate
   * @param outputGate
   *        the input gate to be inserted
   */
  void insertInputGate(final int pos, final ExecutionGate inputGate) {

    if (this.inputGates[pos] != null) {
      throw new IllegalStateException("Input gate at position " + pos + " is not null");
    }

    this.inputGates[pos] = inputGate;
  }

  /**
   * Returns a duplicate of this execution vertex. The duplicated vertex receives
   * a new vertex ID.
   *
   * @return a duplicate of this execution vertex.
   */
  public ExecutionVertex splitVertex() {

    return duplicateVertex(false);
  }

  /**
   * Returns this execution vertex's current execution status.
   *
   * @return this execution vertex's current execution status
   */
  public ExecutionState getExecutionState() {
    return this.executionState.get();
  }

  /**
   * Updates the vertex's current execution state through the job's executor service.
   *
   * @param newExecutionState
   *        the new execution state
   * @param optionalMessage
   *        an optional message related to the state change
   */
  public void updateExecutionStateAsynchronously(final ExecutionState newExecutionState,
      final String optionalMessage) {

    final Runnable command = new Runnable() {

      /**
       * {@inheritDoc}
       */
      @Override
      public void run() {

        updateExecutionState(newExecutionState, optionalMessage);
      }
    };

    this.executionGraph.executeCommand(command);
  }

  /**
   * Updates the vertex's current execution state through the job's executor service.
   *
   * @param newExecutionState
   *        the new execution state
   */
  public void updateExecutionStateAsynchronously(final ExecutionState newExecutionState) {

    updateExecutionStateAsynchronously(newExecutionState, null);
  }

  /**
   * Updates the vertex's current execution state.
   *
   * @param newExecutionState
   *        the new execution state
   */
  public ExecutionState updateExecutionState(final ExecutionState newExecutionState) {
    return updateExecutionState(newExecutionState, null);
  }

  /**
   * Updates the vertex's current execution state.
   *
   * @param newExecutionState
   *        the new execution state
   * @param optionalMessage
   *        an optional message related to the state change
   */
  public ExecutionState updateExecutionState(ExecutionState newExecutionState, final String optionalMessage) {

    if (newExecutionState == null) {
      throw new IllegalArgumentException("Argument newExecutionState must not be null");
    }

    final ExecutionState currentExecutionState = this.executionState.get();
    if (currentExecutionState == ExecutionState.CANCELING) {

      // If we are in CANCELING, ignore state changes to FINISHING
      if (newExecutionState == ExecutionState.FINISHING) {
        return currentExecutionState;
      }

      // Rewrite FINISHED to CANCELED if the task has been marked to be canceled
      if (newExecutionState == ExecutionState.FINISHED) {
        LOG.info("Received transition from CANCELING to FINISHED for vertex " + toString()
          + ", converting it to CANCELED");
        newExecutionState = ExecutionState.CANCELED;
      }
    }

    // Check and save the new execution state
    final ExecutionState previousState = this.executionState.getAndSet(newExecutionState);
    if (previousState == newExecutionState) {
      return previousState;
    }

    // Check the transition
    ExecutionStateTransition.checkTransition(true, toString(), previousState, newExecutionState);

    // Notify the listener objects
    final Iterator<ExecutionListener> it = this.executionListeners.values().iterator();
    while (it.hasNext()) {
      it.next().executionStateChanged(this.executionGraph.getJobID(), this.vertexID, newExecutionState,
        optionalMessage);
    }

    // The vertex was requested to be canceled by another thread
    checkCancelRequestedFlag();

    return previousState;
  }

  public boolean compareAndUpdateExecutionState(final ExecutionState expected, final ExecutionState update) {

    if (update == null) {
      throw new IllegalArgumentException("Argument update must not be null");
    }

    if (!this.executionState.compareAndSet(expected, update)) {
      return false;
    }

    // Check the transition
    ExecutionStateTransition.checkTransition(true, toString(), expected, update);

    // Notify the listener objects
    final Iterator<ExecutionListener> it = this.executionListeners.values().iterator();
    while (it.hasNext()) {
      it.next().executionStateChanged(this.executionGraph.getJobID(), this.vertexID, update,
        null);
    }

    // Check if the vertex was requested to be canceled by another thread
    checkCancelRequestedFlag();

    return true;
  }

  /**
   * Checks if another thread requested the vertex to cancel while it was in state STARTING. If so, the method clears
   * the respective flag and repeats the cancel request.
   */
  private void checkCancelRequestedFlag() {

    if (this.cancelRequested.compareAndSet(true, false)) {
      final TaskCancelResult tsr = cancelTask();
      if (tsr.getReturnCode() != AbstractTaskResult.ReturnCode.SUCCESS
        && tsr.getReturnCode() != AbstractTaskResult.ReturnCode.TASK_NOT_FOUND) {
        LOG.error("Unable to cancel vertex " + this + ": " + tsr.getReturnCode().toString()
          + ((tsr.getDescription() != null) ? (" (" + tsr.getDescription() + ")") : ""));
      }
    }
  }

  /**
   * Assigns the execution vertex with an {@link AllocatedResource}.
   *
   * @param allocatedResource
   *        the resources which are supposed to be allocated to this vertex
   */
  public void setAllocatedResource(final AllocatedResource allocatedResource) {

    if (allocatedResource == null) {
      throw new IllegalArgumentException("Argument allocatedResource must not be null");
    }

    final AllocatedResource previousResource = this.allocatedResource.getAndSet(allocatedResource);
    if (previousResource != null) {
      previousResource.removeVertexFromResource(this);
    }

    allocatedResource.assignVertexToResource(this);

    // Notify all listener objects
    final Iterator<VertexAssignmentListener> it = this.vertexAssignmentListeners.iterator();
    while (it.hasNext()) {
      it.next().vertexAssignmentChanged(this.vertexID, allocatedResource);
    }
  }

  /**
   * Returns the allocated resources assigned to this execution vertex.
   *
   * @return the allocated resources assigned to this execution vertex
   */
  public AllocatedResource getAllocatedResource() {

    return this.allocatedResource.get();
  }

  /**
   * Returns the allocation ID which identifies the resources used
   * by this vertex within the assigned instance.
   *
   * @return the allocation ID which identifies the resources used
   *         by this vertex within the assigned instance or <code>null</code> if the instance is still assigned to a
   *         {@link eu.stratosphere.nephele.instance.DummyInstance}.
   */
  public AllocationID getAllocationID() {
    return this.allocationID;
  }

  /**
   * Returns the ID of this execution vertex.
   *
   * @return the ID of this execution vertex
   */
  public ExecutionVertexID getID() {
    return this.vertexID;
  }

  /**
   * Returns the number of predecessors, i.e. the number of vertices
   * which connect to this vertex.
   *
   * @return the number of predecessors
   */
  public int getNumberOfPredecessors() {

    int numberOfPredecessors = 0;

    for (int i = 0; i < this.inputGates.length; ++i) {
      numberOfPredecessors += this.inputGates[i].getNumberOfEdges();
    }

    return numberOfPredecessors;
  }

  /**
   * Returns the number of successors, i.e. the number of vertices
   * this vertex is connected to.
   *
   * @return the number of successors
   */
  public int getNumberOfSuccessors() {

    int numberOfSuccessors = 0;

    for (int i = 0; i < this.outputGates.length; ++i) {
      numberOfSuccessors += this.outputGates[i].getNumberOfEdges();
    }

    return numberOfSuccessors;
  }

  public ExecutionVertex getPredecessor(int index) {

    if (index < 0) {
      throw new IllegalArgumentException("Argument index must be greather or equal to 0");
    }

    for (int i = 0; i < this.inputGates.length; ++i) {

      final ExecutionGate inputGate = this.inputGates[i];
      final int numberOfEdges = inputGate.getNumberOfEdges();

      if (index >= 0 && index < numberOfEdges) {

        final ExecutionEdge edge = inputGate.getEdge(index);
        return edge.getOutputGate().getVertex();
      }
      index -= numberOfEdges;
    }

    return null;
  }

  public ExecutionVertex getSuccessor(int index) {

    if (index < 0) {
      throw new IllegalArgumentException("Argument index must be greather or equal to 0");
    }

    for (int i = 0; i < this.outputGates.length; ++i) {

      final ExecutionGate outputGate = this.outputGates[i];
      final int numberOfEdges = outputGate.getNumberOfEdges();

      if (index >= 0 && index < numberOfEdges) {

        final ExecutionEdge edge = outputGate.getEdge(index);
        return edge.getInputGate().getVertex();
      }
      index -= numberOfEdges;
    }

    return null;

  }

  /**
   * Checks if this vertex is an input vertex in its stage, i.e. has either no
   * incoming connections or only incoming connections to group vertices in a lower stage.
   *
   * @return <code>true</code> if this vertex is an input vertex, <code>false</code> otherwise
   */
  public boolean isInputVertex() {

    return this.groupVertex.isInputVertex();
  }

  /**
   * Checks if this vertex is an output vertex in its stage, i.e. has either no
   * outgoing connections or only outgoing connections to group vertices in a higher stage.
   *
   * @return <code>true</code> if this vertex is an output vertex, <code>false</code> otherwise
   */
  public boolean isOutputVertex() {

    return this.groupVertex.isOutputVertex();
  }

  /**
   * Returns the index of this vertex in the vertex group.
   *
   * @return the index of this vertex in the vertex group
   */
  public int getIndexInVertexGroup() {

    return this.indexInVertexGroup;
  }

  /**
   * Sets the vertex' index in the vertex group.
   *
   * @param indexInVertexGroup
   *        the vertex' index in the vertex group
   */
  void setIndexInVertexGroup(final int indexInVertexGroup) {

    this.indexInVertexGroup = indexInVertexGroup;
  }

  /**
   * Returns the number of output gates attached to this vertex.
   *
   * @return the number of output gates attached to this vertex
   */
  public int getNumberOfOutputGates() {

    return this.outputGates.length;
  }

  /**
   * Returns the output gate with the given index.
   *
   * @param index
   *        the index of the output gate to return
   * @return the output gate with the given index
   */
  public ExecutionGate getOutputGate(final int index) {

    return this.outputGates[index];
  }

  /**
   * Returns the number of input gates attached to this vertex.
   *
   * @return the number of input gates attached to this vertex
   */
  public int getNumberOfInputGates() {

    return this.inputGates.length;
  }

  /**
   * Returns the input gate with the given index.
   *
   * @param index
   *        the index of the input gate to return
   * @return the input gate with the given index
   */
  public ExecutionGate getInputGate(final int index) {

    return this.inputGates[index];
  }

  /**
   * Deploys and starts the task represented by this vertex
   * on the assigned instance.
   *
   * @return the result of the task submission attempt
   */
  public TaskSubmissionResult startTask() {

    final AllocatedResource ar = this.allocatedResource.get();

    if (ar == null) {
      final TaskSubmissionResult result = new TaskSubmissionResult(getID(),
        AbstractTaskResult.ReturnCode.NO_INSTANCE);
      result.setDescription("Assigned instance of vertex " + this.toString() + " is null!");
      return result;
    }

    final List<TaskDeploymentDescriptor> tasks = new SerializableArrayList<TaskDeploymentDescriptor>();
    tasks.add(constructDeploymentDescriptor());

    try {
      final List<TaskSubmissionResult> results = ar.getInstance().submitTasks(tasks);

      return results.get(0);

    } catch (IOException e) {
      final TaskSubmissionResult result = new TaskSubmissionResult(getID(),
        AbstractTaskResult.ReturnCode.IPC_ERROR);
      result.setDescription(StringUtils.stringifyException(e));
      return result;
    }
  }

  /**
   * Kills and removes the task represented by this vertex from the instance it is currently running on. If the
   * corresponding task is not in the state <code>RUNNING</code>, this call will be ignored. If the call has been
   * executed
   * successfully, the task will change the state <code>FAILED</code>.
   *
   * @return the result of the task kill attempt
   */
  public TaskKillResult killTask() {

    final ExecutionState state = this.executionState.get();

    if (state != ExecutionState.RUNNING) {
      final TaskKillResult result = new TaskKillResult(getID(), AbstractTaskResult.ReturnCode.ILLEGAL_STATE);
      result.setDescription("Vertex " + this.toString() + " is in state " + state);
      return result;
    }

    final AllocatedResource ar = this.allocatedResource.get();

    if (ar == null) {
      final TaskKillResult result = new TaskKillResult(getID(), AbstractTaskResult.ReturnCode.NO_INSTANCE);
      result.setDescription("Assigned instance of vertex " + this.toString() + " is null!");
      return result;
    }

    try {
      return ar.getInstance().killTask(this.vertexID);
    } catch (IOException e) {
      final TaskKillResult result = new TaskKillResult(getID(), AbstractTaskResult.ReturnCode.IPC_ERROR);
      result.setDescription(StringUtils.stringifyException(e));
      return result;
    }
  }

  /**
   * Cancels and removes the task represented by this vertex
   * from the instance it is currently running on. If the task
   * is not currently running, its execution state is simply
   * updated to <code>CANCELLED</code>.
   *
   * @return the result of the task cancel attempt
   */
  public TaskCancelResult cancelTask() {

    while (true) {

      final ExecutionState previousState = this.executionState.get();

      if (previousState == ExecutionState.CANCELED) {
        return new TaskCancelResult(getID(), AbstractTaskResult.ReturnCode.SUCCESS);
      }

      if (previousState == ExecutionState.FAILED) {
        return new TaskCancelResult(getID(), AbstractTaskResult.ReturnCode.SUCCESS);
      }

      if (previousState == ExecutionState.FINISHED) {
        return new TaskCancelResult(getID(), AbstractTaskResult.ReturnCode.SUCCESS);
      }

      // The vertex has already received a cancel request
      if (previousState == ExecutionState.CANCELING) {
        return new TaskCancelResult(getID(), ReturnCode.SUCCESS);
      }

      // Do not trigger the cancel request when vertex is in state STARTING, this might cause a race between RPC
      // calls.
      if (previousState == ExecutionState.STARTING) {

        this.cancelRequested.set(true);

        // We had a race, so we unset the flag and take care of cancellation ourselves
        if (this.executionState.get() != ExecutionState.STARTING) {
          this.cancelRequested.set(false);
          continue;
        }

        return new TaskCancelResult(getID(), AbstractTaskResult.ReturnCode.SUCCESS);
      }

      // Check if we had a race. If state change is accepted, send cancel request
      if (compareAndUpdateExecutionState(previousState, ExecutionState.CANCELING)) {

        if (this.groupVertex.getStageNumber() != this.executionGraph.getIndexOfCurrentExecutionStage()) {
          // Set to canceled directly
          updateExecutionState(ExecutionState.CANCELED, null);
          return new TaskCancelResult(getID(), AbstractTaskResult.ReturnCode.SUCCESS);
        }

        if (previousState != ExecutionState.RUNNING && previousState != ExecutionState.FINISHING) {
          // Set to canceled directly
          updateExecutionState(ExecutionState.CANCELED, null);
          return new TaskCancelResult(getID(), AbstractTaskResult.ReturnCode.SUCCESS);
        }

        final AllocatedResource ar = this.allocatedResource.get();

        if (ar == null) {
          final TaskCancelResult result = new TaskCancelResult(getID(),
            AbstractTaskResult.ReturnCode.NO_INSTANCE);
          result.setDescription("Assigned instance of vertex " + this.toString() + " is null!");
          return result;
        }

        try {
          return ar.getInstance().cancelTask(this.vertexID);

        } catch (IOException e) {
          final TaskCancelResult result = new TaskCancelResult(getID(),
            AbstractTaskResult.ReturnCode.IPC_ERROR);
          result.setDescription(StringUtils.stringifyException(e));
          return result;
        }
      }
    }
  }

  /**
   * Returns the {@link ExecutionGraph} this vertex belongs to.
   *
   * @return the {@link ExecutionGraph} this vertex belongs to
   */
  public ExecutionGraph getExecutionGraph() {

    return this.executionGraph;
  }


  @Override
  public String toString() {

    final StringBuilder sb = new StringBuilder(this.groupVertex.getName());
    sb.append(" (");
    sb.append(this.indexInVertexGroup + 1);
    sb.append('/');
    sb.append(this.groupVertex.getCurrentNumberOfGroupMembers());
    sb.append(')');

    return sb.toString();
  }

  /**
   * Returns the task represented by this vertex has
   * a retry attempt left in case of an execution
   * failure.
   *
   * @return <code>true</code> if the task has a retry attempt left, <code>false</code> otherwise
   */
  @Deprecated
  public boolean hasRetriesLeft() {
    if (this.retriesLeft.get() <= 0) {
      return false;
    }
    return true;
  }

  /**
   * Decrements the number of retries left and checks whether another attempt to run the task is possible.
   *
   * @return <code>true</code>if the task represented by this vertex can be started at least once more,
   *         <code>false/<code> otherwise
   */
  public boolean decrementRetriesLeftAndCheck() {

    return (this.retriesLeft.decrementAndGet() > 0);
  }

  /**
   * Registers the {@link VertexAssignmentListener} object for this vertex. This object
   * will be notified about reassignments of this vertex to another instance.
   *
   * @param vertexAssignmentListener
   *        the object to be notified about reassignments of this vertex to another instance
   */
  public void registerVertexAssignmentListener(final VertexAssignmentListener vertexAssignmentListener) {

    this.vertexAssignmentListeners.addIfAbsent(vertexAssignmentListener);
  }

  /**
   * Unregisters the {@link VertexAssignmentListener} object for this vertex. This object
   * will no longer be notified about reassignments of this vertex to another instance.
   *
   * @param vertexAssignmentListener
   *        the listener to be unregistered
   */
  public void unregisterVertexAssignmentListener(final VertexAssignmentListener vertexAssignmentListener) {

    this.vertexAssignmentListeners.remove(vertexAssignmentListener);
  }


  /**
   * Registers the {@link ExecutionListener} object for this vertex. This object
   * will be notified about particular events during the vertex's lifetime.
   *
   * @param executionListener
   *        the object to be notified about particular events during the vertex's lifetime
   */
  public void registerExecutionListener(final ExecutionListener executionListener) {

    final Integer priority = Integer.valueOf(executionListener.getPriority());

    if (priority.intValue() < 0) {
      LOG.error("Priority for execution listener " + executionListener.getClass() + " must be non-negative.");
      return;
    }

    final ExecutionListener previousValue = this.executionListeners.putIfAbsent(priority, executionListener);

    if (previousValue != null) {
      LOG.error("Cannot register " + executionListener.getClass() + " as an execution listener. Priority "
        + priority.intValue() + " is already taken.");
    }
  }

  /**
   * Unregisters the {@link ExecutionListener} object for this vertex. This object
   * will no longer be notified about particular events during the vertex's lifetime.
   *
   * @param executionListener
   *        the object to be unregistered
   */
  public void unregisterExecutionListener(final ExecutionListener executionListener) {

    this.executionListeners.remove(Integer.valueOf(executionListener.getPriority()));
  }


  /**
   * Sets the {@link ExecutionPipeline} this vertex shall be part of.
   *
   * @param executionPipeline
   *        the execution pipeline this vertex shall be part of
   */
  void setExecutionPipeline(final ExecutionPipeline executionPipeline) {

    final ExecutionPipeline oldPipeline = this.executionPipeline.getAndSet(executionPipeline);
    if (oldPipeline != null) {
      oldPipeline.removeFromPipeline(this);
    }

    executionPipeline.addToPipeline(this);
  }

  /**
   * Returns the {@link ExecutionPipeline} this vertex is part of.
   *
   * @return the execution pipeline this vertex is part of
   */
  public ExecutionPipeline getExecutionPipeline() {

    return this.executionPipeline.get();
  }

  /**
   * Constructs a new task deployment descriptor for this vertex.
   *
   * @return a new task deployment descriptor for this vertex
   */
  public TaskDeploymentDescriptor constructDeploymentDescriptor() {

    final SerializableArrayList<GateDeploymentDescriptor> ogd = new SerializableArrayList<GateDeploymentDescriptor>(
      this.outputGates.length);
    for (int i = 0; i < this.outputGates.length; ++i) {

      final ExecutionGate eg = this.outputGates[i];
      final List<ChannelDeploymentDescriptor> cdd = new ArrayList<ChannelDeploymentDescriptor>(
        eg.getNumberOfEdges());
      final int numberOfOutputChannels = eg.getNumberOfEdges();
      for (int j = 0; j < numberOfOutputChannels; ++j) {

        final ExecutionEdge ee = eg.getEdge(j);
        cdd.add(new ChannelDeploymentDescriptor(ee.getOutputChannelID(), ee.getInputChannelID()));
      }

      ogd.add(new GateDeploymentDescriptor(eg.getGateID(), eg.getChannelType(), cdd));
    }

    final SerializableArrayList<GateDeploymentDescriptor> igd = new SerializableArrayList<GateDeploymentDescriptor>(
      this.inputGates.length);
    for (int i = 0; i < this.inputGates.length; ++i) {

      final ExecutionGate eg = this.inputGates[i];
      final List<ChannelDeploymentDescriptor> cdd = new ArrayList<ChannelDeploymentDescriptor>(
        eg.getNumberOfEdges());
      final int numberOfInputChannels = eg.getNumberOfEdges();
      for (int j = 0; j < numberOfInputChannels; ++j) {

        final ExecutionEdge ee = eg.getEdge(j);
        cdd.add(new ChannelDeploymentDescriptor(ee.getOutputChannelID(), ee.getInputChannelID()));
      }

      igd.add(new GateDeploymentDescriptor(eg.getGateID(), eg.getChannelType(), cdd));
    }

    final TaskDeploymentDescriptor tdd = new TaskDeploymentDescriptor(this.executionGraph.getJobID(),
      this.vertexID, this.groupVertex.getName(), this.indexInVertexGroup,
      this.groupVertex.getCurrentNumberOfGroupMembers(), this.executionGraph.getJobConfiguration(),
      this.groupVertex.getConfiguration(), this.groupVertex.getInvokableClass(), ogd,
      igd);

    return tdd;
  }
}
TOP

Related Classes of eu.stratosphere.nephele.executiongraph.ExecutionVertex

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.