Package eu.stratosphere.nephele.jobgraph

Source Code of eu.stratosphere.nephele.jobgraph.AbstractJobVertex

/***********************************************************************************************************************
* 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.jobgraph;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;

import eu.stratosphere.configuration.Configuration;
import eu.stratosphere.configuration.IllegalConfigurationException;
import eu.stratosphere.core.io.IOReadableWritable;
import eu.stratosphere.core.io.StringRecord;
import eu.stratosphere.nephele.execution.librarycache.LibraryCacheManager;
import eu.stratosphere.runtime.io.channels.ChannelType;
import eu.stratosphere.nephele.template.AbstractInvokable;
import eu.stratosphere.nephele.util.EnumUtils;
import eu.stratosphere.util.StringUtils;

/**
* An abstract base class for a job vertex in Nephele.
*
*/
public abstract class AbstractJobVertex implements IOReadableWritable {

  private static final String DEFAULT_NAME = "(unnamed vertex)";
 
  /**
   * List of outgoing edges.
   */
  private final ArrayList<JobEdge> forwardEdges = new ArrayList<JobEdge>();

  /**
   * List of incoming edges.
   */
  private final ArrayList<JobEdge> backwardEdges = new ArrayList<JobEdge>();

  /**
   * The name of the vertex or task, respectively.
   */
  private String name;

  /**
   * The ID of the vertex.
   */
  private final JobVertexID id;

  /**
   * The graph this vertex belongs to.
   */
  private final JobGraph jobGraph;

  /**
   * Number of subtasks to split this task into at runtime.
   */
  private int numberOfSubtasks = -1;

  /**
   * The type of instance to be assigned to this task at runtime.
   */
  private String instanceType = null;

  /**
   * Number of subtasks to share the same instance at runtime.
   */
  private int numberOfSubtasksPerInstance = -1;

  /**
   * Number of retries in case of an error before the task represented by this vertex is considered as failed.
   */
  private int numberOfExecutionRetries = -1;

  /**
   * Other task to share a (set of) of instances with at runtime.
   */
  private AbstractJobVertex vertexToShareInstancesWith = null;

  /**
   * Custom configuration passed to the assigned task at runtime.
   */
  private Configuration configuration = new Configuration();

  /**
   * The class of the invokable.
   */
  protected Class<? extends AbstractInvokable> invokableClass = null;

  /**
   * Constructs a new job vertex and assigns it with the given name.
   *
   * @param name
   *        the name of the new job vertex
   * @param id
   *        the ID of this vertex
   * @param jobGraph
   *        the job graph this vertex belongs to
   */
  protected AbstractJobVertex(final String name, final JobVertexID id, final JobGraph jobGraph) {
    this.name = name == null ? DEFAULT_NAME : name;
    this.id = (id == null) ? new JobVertexID() : id;
    this.jobGraph = jobGraph;
  }

  /**
   * Connects the job vertex to the specified job vertex.
   *
   * @param vertex
   *        the vertex this vertex should connect to
   * @throws JobGraphDefinitionException
   *         thrown if the given vertex cannot be connected to <code>vertex</code> in the requested manner
   */
  public void connectTo(final AbstractJobVertex vertex) throws JobGraphDefinitionException {
    this.connectTo(vertex, null, -1, -1, DistributionPattern.BIPARTITE);
  }

  /**
   * Connects the job vertex to the specified job vertex.
   *
   * @param vertex
   *        the vertex this vertex should connect to
   * @param indexOfOutputGate
   *        index of the producing task's output gate to be used, <code>-1</code> will determine the next free index
   *        number
   * @param indexOfInputGate
   *        index of the consuming task's input gate to be used, <code>-1</code> will determine the next free index
   *        number
   * @throws JobGraphDefinitionException
   *         thrown if the given vertex cannot be connected to <code>vertex</code> in the requested manner
   */
  public void connectTo(final AbstractJobVertex vertex, final int indexOfOutputGate, final int indexOfInputGate)
      throws JobGraphDefinitionException {
    this.connectTo(vertex, null, indexOfOutputGate, indexOfInputGate, DistributionPattern.BIPARTITE);
  }

  /**
   * Connects the job vertex to the specified job vertex.
   *
   * @param vertex
   *        the vertex this vertex should connect to
   * @param channelType
   *        the channel type the two vertices should be connected by at runtime
   * @param compressionLevel
   *        the compression level the corresponding channel should have at runtime
   * @throws JobGraphDefinitionException
   *         thrown if the given vertex cannot be connected to <code>vertex</code> in the requested manner
   */
  public void connectTo(final AbstractJobVertex vertex, final ChannelType channelType) throws JobGraphDefinitionException {
    this.connectTo(vertex, channelType, -1, -1, DistributionPattern.BIPARTITE);
  }

  /**
   * Connects the job vertex to the specified job vertex.
   *
   * @param vertex
   *        the vertex this vertex should connect to
   * @param channelType
   *        the channel type the two vertices should be connected by at runtime
   * @param compressionLevel
   *        the compression level the corresponding channel should have at runtime
   * @throws JobGraphDefinitionException
   *         thrown if the given vertex cannot be connected to <code>vertex</code> in the requested manner
   */
  public void connectTo(final AbstractJobVertex vertex, final ChannelType channelType,
      final DistributionPattern distributionPattern)
      throws JobGraphDefinitionException {
    this.connectTo(vertex, channelType, -1, -1, distributionPattern);
  }

  /**
   * Connects the job vertex to the specified job vertex.
   *
   * @param vertex
   *        the vertex this vertex should connect to
   * @param channelType
   *        the channel type the two vertices should be connected by at runtime
   * @param compressionLevel
   *        the compression level the corresponding channel should have at runtime
   * @param indexOfOutputGate
   *        index of the producing task's output gate to be used, <code>-1</code> will determine the next free index
   *        number
   * @param indexOfInputGate
   *        index of the consuming task's input gate to be used, <code>-1</code> will determine the next free index
   *        number
   * @throws JobGraphDefinitionException
   *         thrown if the given vertex cannot be connected to <code>vertex</code> in the requested manner
   */
  public void connectTo(final AbstractJobVertex vertex, final ChannelType channelType, int indexOfOutputGate, int indexOfInputGate,
      DistributionPattern distributionPattern)
      throws JobGraphDefinitionException {

    if (vertex == null) {
      throw new JobGraphDefinitionException("Target vertex is null!");
    }

    if (indexOfOutputGate == -1) {
      indexOfOutputGate = getFirstFreeOutputGateIndex();
    }

    // Make sure the array is big enough
    for (int i = this.forwardEdges.size(); i <= indexOfOutputGate; i++) {
      this.forwardEdges.add(null);
    }

    if (this.forwardEdges.get(indexOfOutputGate) != null) {
      throw new JobGraphDefinitionException("Source vertex " + this.name + " already has an edge at index "
        + indexOfOutputGate);
    }

    if (indexOfInputGate == -1) {
      indexOfInputGate = vertex.getFirstFreeInputGateIndex();
    } else {
      if (vertex.getBackwardConnection(indexOfInputGate) != null) {
        throw new JobGraphDefinitionException("Target vertex " + vertex.getName()
          + " already has an edge at index " + indexOfInputGate);
      }
    }

    // Add new edge
    this.forwardEdges.set(indexOfOutputGate, new JobEdge(vertex, channelType, indexOfInputGate,
      distributionPattern));
    vertex.connectBacklink(this, channelType, indexOfOutputGate, indexOfInputGate,
      distributionPattern);
  }

  /**
   * Returns the index of this vertex's first free output gate.
   *
   * @return the index of the first free output gate
   */
  protected int getFirstFreeOutputGateIndex() {

    for (int i = 0; i < this.forwardEdges.size(); i++) {

      if (this.forwardEdges.get(i) == null) {
        return i;
      }
    }

    return this.forwardEdges.size();
  }

  /**
   * Returns the index of this vertex's first free input gate.
   *
   * @return the index of the first free input gate
   */
  protected int getFirstFreeInputGateIndex() {

    for (int i = 0; i < this.backwardEdges.size(); i++) {

      if (this.backwardEdges.get(i) == null) {
        return i;
      }
    }

    return this.backwardEdges.size();
  }

  /**
   * Creates a backward link from a connected job vertex.
   *
   * @param vertex
   *        the job vertex to connect to
   * @param channelType
   *        the channel type the two vertices should be connected by at runtime
   * @param compressionLevel
   *        the compression level the corresponding channel should have at runtime
   * @param indexOfOutputGate
   *        index of the producing task's output gate to be used
   * @param indexOfInputGate
   *        index of the consuming task's input gate to be used
   */
  private void connectBacklink(final AbstractJobVertex vertex, final ChannelType channelType,
      final int indexOfOutputGate, final int indexOfInputGate,
      DistributionPattern distributionPattern) {

    // Make sure the array is big enough
    for (int i = this.backwardEdges.size(); i <= indexOfInputGate; i++) {
      this.backwardEdges.add(null);
    }

    this.backwardEdges.set(indexOfInputGate, new JobEdge(vertex, channelType, indexOfOutputGate,
      distributionPattern));
  }

  /**
   * Sets the name of the vertex.
   *
   * @param name
   *        The name of the vertex.
   */
  public void setName(String name) {
    this.name = name;
  }
 
  /**
   * Returns the name of the vertex.
   *
   * @return the name of the vertex or <code>null</code> if no name is set.
   */
  public String getName() {
    return this.name;
  }

  /**
   * Returns the number of forward connections.
   *
   * @return the number of forward connections
   */
  public int getNumberOfForwardConnections() {
    return this.forwardEdges.size();
  }

  /**
   * Returns the number of backward connections.
   *
   * @return the number of backward connections
   */
  public int getNumberOfBackwardConnections() {
    return this.backwardEdges.size();
  }

  /**
   * Returns the forward edge with index <code>index</code>.
   *
   * @param index
   *        the index of the edge
   * @return the forward edge or <code>null</code> if no edge exists at the specified index.
   */
  public JobEdge getForwardConnection(final int index) {

    if (index < this.forwardEdges.size()) {
      return this.forwardEdges.get(index);
    }

    return null;
  }

  /**
   * Returns the backward edge with index <code>index</code>.
   *
   * @param index
   *        the index of the edge
   * @return the backward edge or <code>null</code> if no edge exists at the specified index
   */
  public JobEdge getBackwardConnection(final int index) {

    if (index < this.backwardEdges.size()) {
      return this.backwardEdges.get(index);
    }

    return null;
  }

  /**
   * Returns the index of the edge which is used to connect the given job vertex to this job vertex.
   *
   * @param jv
   *        the connected job vertex
   * @return the index of the edge which is used to connect the given job vertex to this job vertex or -1 if the given
   *         vertex is not connected to this job vertex
   */
  /*
   * public int getBackwardConnectionIndex(AbstractJobVertex jv) {
   * if(jv == null) {
   * return -1;
   * }
   * final Iterator<JobEdge> it = this.backwardEdges.iterator();
   * int i = 0;
   * while(it.hasNext()) {
   * final JobEdge edge = it.next();
   * if(edge.getConnectedVertex() == jv) {
   * return i;
   * }
   * i++;
   * }
   * return -1;
   * }
   */

  /**
   * Returns the ID of this job vertex.
   *
   * @return the ID of this job vertex
   */
  public JobVertexID getID() {
    return this.id;
  }


  @SuppressWarnings("unchecked")
  @Override
  public void read(final DataInput in) throws IOException {

    if (jobGraph == null) {
      throw new IOException("jobGraph is null, cannot deserialize");
    }

    // Read instance type
    this.instanceType = StringRecord.readString(in);

    // Read number of subtasks
    this.numberOfSubtasks = in.readInt();

    // Read number of subtasks per instance
    this.numberOfSubtasksPerInstance = in.readInt();

    // Number of execution retries
    this.numberOfExecutionRetries = in.readInt();

    // Read vertex to share instances with
    if (in.readBoolean()) {
      final JobVertexID id = new JobVertexID();
      id.read(in);
      final AbstractJobVertex vertexToShareInstancesWith = this.jobGraph.findVertexByID(id);
      if (vertexToShareInstancesWith == null) {
        throw new IOException("Cannot find vertex with id " + id + " share instances with");
      }

      this.vertexToShareInstancesWith = vertexToShareInstancesWith;
    }

    // Find the class loader for the job
    final ClassLoader cl = LibraryCacheManager.getClassLoader(this.getJobGraph().getJobID());
    if (cl == null) {
      throw new IOException("Cannot find class loader for vertex " + getID());
    }

    // Re-instantiate the configuration object with the correct class loader and read the configuration
    this.configuration = new Configuration(cl);
    this.configuration.read(in);

    // Read number of forward edges
    final int numForwardEdges = in.readInt();

    // Now reconnect to other vertices via the reconstruction map
    final JobVertexID tmpID = new JobVertexID();
    for (int i = 0; i < numForwardEdges; i++) {
      if (in.readBoolean()) {
        tmpID.read(in);
        final AbstractJobVertex jv = jobGraph.findVertexByID(tmpID);
        if (jv == null) {
          throw new IOException("Cannot find vertex with id " + tmpID);
        }

        final ChannelType channelType = EnumUtils.readEnum(in, ChannelType.class);
        final DistributionPattern distributionPattern = EnumUtils.readEnum(in, DistributionPattern.class);
        final int indexOfInputGate = in.readInt();

        try {
          this.connectTo(jv, channelType, i, indexOfInputGate, distributionPattern);
        } catch (JobGraphDefinitionException e) {
          throw new IOException(StringUtils.stringifyException(e));
        }
      } else {
        this.forwardEdges.add(null);
      }
    }

    // Read the invokable class
    final boolean isNotNull = in.readBoolean();
    if (!isNotNull) {
      return;
    }

    // Read the name of the expected class
    final String className = StringRecord.readString(in);

    try {
      this.invokableClass = (Class<? extends AbstractInvokable>) Class.forName(className, true, cl);
    } catch (ClassNotFoundException cnfe) {
      throw new IOException("Class " + className + " not found in one of the supplied jar files: "
        + StringUtils.stringifyException(cnfe));
    }
  }


  @Override
  public void write(final DataOutput out) throws IOException {

    // Instance type
    StringRecord.writeString(out, this.instanceType);

    // Number of subtasks
    out.writeInt(this.numberOfSubtasks);

    // Number of subtasks per instance
    out.writeInt(this.numberOfSubtasksPerInstance);

    // Number of execution retries
    out.writeInt(this.numberOfExecutionRetries);

    // Vertex to share instance with
    if (this.vertexToShareInstancesWith != null) {
      out.writeBoolean(true);
      this.vertexToShareInstancesWith.getID().write(out);
    } else {
      out.writeBoolean(false);
    }

    // Write the configuration
    this.configuration.write(out);

    // We ignore the backward edges and connect them when we reconstruct the graph on the remote side, only write
    // number of forward edges
    out.writeInt(this.forwardEdges.size());

    // Now output the IDs of the vertices this vertex is connected to
    for (int i = 0; i < this.forwardEdges.size(); i++) {
      final JobEdge edge = this.forwardEdges.get(i);
      if (edge == null) {
        out.writeBoolean(false);
      } else {
        out.writeBoolean(true);
        edge.getConnectedVertex().getID().write(out);
        EnumUtils.writeEnum(out, edge.getChannelType());
        EnumUtils.writeEnum(out, edge.getDistributionPattern());
        out.writeInt(edge.getIndexOfInputGate());
      }
    }

    // Write the invokable class
    if (this.invokableClass == null) {
      out.writeBoolean(false);
      return;
    }

    out.writeBoolean(true);

    // Write out the name of the class
    StringRecord.writeString(out, this.invokableClass.getName());
  }

  /**
   * Returns the job graph this job vertex belongs to.
   *
   * @return the job graph this job vertex belongs to or <code>null</code> if no job graph has been set yet
   */
  public JobGraph getJobGraph() {
    return this.jobGraph;
  }

  /**
   * Sets the number of subtasks the task this vertex represents should be split into at runtime.
   *
   * @param numberOfSubtasks
   *        the number of subtasks this vertex represents should be split into at runtime
   */
  public void setNumberOfSubtasks(final int numberOfSubtasks) {
    this.numberOfSubtasks = numberOfSubtasks;
  }

  /**
   * Returns the number of subtasks the task this vertex represents should be split into at runtime.
   *
   * @return the number of subtasks this vertex represents should be split into at runtime, <code>-1</code> if
   *         unspecified
   */
  public int getNumberOfSubtasks() {
    return this.numberOfSubtasks;
  }

  /**
   * Sets the number of retries in case of an error before the task represented by this vertex is considered as
   * failed.
   *
   * @param numberOfExecutionRetries
   *        the number of retries in case of an error before the task represented by this vertex is considered as
   *        failed
   */
  public void setNumberOfExecutionRetries(final int numberOfExecutionRetries) {
    this.numberOfExecutionRetries = numberOfExecutionRetries;
  }

  /**
   * Returns the number of retries in case of an error before the task represented by this vertex is considered as
   * failed.
   *
   * @return the number of retries in case of an error before the task represented by this vertex is considered as
   *         failed or <code>-1</code> if unspecified
   */
  public int getNumberOfExecutionRetries() {
    return this.numberOfExecutionRetries;
  }

  /**
   * Sets the instance type the task this vertex represents should run on.
   *
   * @param instanceType
   *        the instance type the task this vertex represents should run on
   */
  public void setInstanceType(final String instanceType) {
    this.instanceType = instanceType;
  }

  /**
   * Returns the instance type the task this vertex represents should run on.
   *
   * @return the instance type the task this vertex represents should run on, <code>null</code> if unspecified
   */
  public String getInstanceType() {
    return this.instanceType;
  }

  /**
   * Sets the number of subtasks that should be assigned to the same instance.
   *
   * @param numberOfSubtasksPerInstance
   *        the number of subtasks that should be assigned to the same instance
   */
  public void setNumberOfSubtasksPerInstance(final int numberOfSubtasksPerInstance) {
    this.numberOfSubtasksPerInstance = numberOfSubtasksPerInstance;
  }

  /**
   * Returns the number of subtasks that should be assigned to the same instance, <code>-1</code> if undefined.
   *
   * @return the number of subtasks that should be assigned to the same instance, <code>-1</code> if undefined
   */
  public int getNumberOfSubtasksPerInstance() {
    return this.numberOfSubtasksPerInstance;
  }

  /**
   * Sets the vertex this vertex should share its instances with at runtime.
   *
   * @param vertex
   *        the vertex this vertex should share its instances with at runtime
   */
  public void setVertexToShareInstancesWith(final AbstractJobVertex vertex) {
    this.vertexToShareInstancesWith = vertex;
  }

  /**
   * Returns the vertex this vertex should share its instance with at runtime.
   *
   * @return the vertex this vertex should share its instance with at runtime, <code>null</code> if undefined
   */
  public AbstractJobVertex getVertexToShareInstancesWith() {
    return this.vertexToShareInstancesWith;
  }

  /**
   * Returns the vertex's configuration object which can be used to pass custom settings to the task at runtime.
   *
   * @return the vertex's configuration object
   */
  public Configuration getConfiguration() {
    return this.configuration;
  }

  /**
   * Performs task specific checks if the
   * respective task has been configured properly.
   *
   * @param invokable
   *        an instance of the task this vertex represents
   * @throws IllegalConfigurationException
   *         thrown if the respective tasks is not configured properly
   */
  public void checkConfiguration(final AbstractInvokable invokable) throws IllegalConfigurationException {

    if (invokable == null) {
      throw new IllegalArgumentException("Argument invokable is null");
    }

    // see if the task itself has a valid configuration
    // because this is user code running on the master, we embed it in a catch-all block
    try {
      invokable.checkConfiguration();
    } catch (IllegalConfigurationException icex) {
      throw icex; // simply forward
    } catch (Throwable t) {
      throw new IllegalConfigurationException("Checking the invokable's configuration caused an error: "
        + StringUtils.stringifyException(t));
    }
  }

  /**
   * Returns the minimum number of subtasks the respective task
   * must be split into at runtime.
   *
   * @param invokable
   *        an instance of the task this vertex represents
   * @return the minimum number of subtasks the respective task must be split into at runtime
   */
  public int getMinimumNumberOfSubtasks(final AbstractInvokable invokable) {

    if (invokable == null) {
      throw new IllegalArgumentException("Argument invokable is null");
    }

    return invokable.getMinimumNumberOfSubtasks();
  }

  /**
   * Returns the maximum number of subtasks the respective task
   * can be split into at runtime.
   *
   * @param invokable
   *        an instance of the task this vertex represents
   * @return the maximum number of subtasks the respective task can be split into at runtime, <code>-1</code> for
   *         infinity
   */
  public int getMaximumNumberOfSubtasks(final AbstractInvokable invokable) {

    if (invokable == null) {
      throw new IllegalArgumentException("Argument invokable is null");
    }

    return invokable.getMaximumNumberOfSubtasks();
  }

  /**
   * Returns the invokable class which represents the task of this vertex
   *
   * @return the invokable class, <code>null</code> if it is not set
   */
  public Class<? extends AbstractInvokable> getInvokableClass() {

    return this.invokableClass;
  }
 
  @Override
  public String toString() {
    return this.name + " (" + this.invokableClass + ')';
  }
}
TOP

Related Classes of eu.stratosphere.nephele.jobgraph.AbstractJobVertex

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.