/***********************************************************************************************************************
* 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.managementgraph;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import eu.stratosphere.core.io.IOReadableWritable;
import eu.stratosphere.nephele.execution.ExecutionState;
import eu.stratosphere.runtime.io.channels.ChannelType;
import eu.stratosphere.nephele.util.EnumUtils;
import eu.stratosphere.util.StringUtils;
/**
* This class implements a management group vertex of a {@link ManagementGraph}. A management group vertex is derived
* from the type of group vertices Nephele uses in its internal scheduling structures.
* <p>
* This class is not thread-safe.
*
*/
public final class ManagementGroupVertex extends ManagementAttachment implements IOReadableWritable {
/**
* The ID of the management group vertex.
*/
private final ManagementGroupVertexID id;
/**
* The name of the management group vertex.
*/
private final String name;
/**
* The stage this management group vertex belongs to.
*/
private final ManagementStage stage;
/**
* The list of {@link ManagementVertex} contained in this group vertex.
*/
private final List<ManagementVertex> groupMembers = new ArrayList<ManagementVertex>();
/**
* The list of group edges which originate from this group vertex.
*/
private final List<ManagementGroupEdge> forwardEdges = new ArrayList<ManagementGroupEdge>();
/**
* The list of group edges which arrive at this group vertex.
*/
private final List<ManagementGroupEdge> backwardEdges = new ArrayList<ManagementGroupEdge>();
/**
* Constructs a new management group vertex.
*
* @param stage
* the stage this group vertex belongs to
* @param name
* the name of the new management group vertex
*/
public ManagementGroupVertex(final ManagementStage stage, final String name) {
this(stage, new ManagementGroupVertexID(), name);
}
/**
* Constructs a new management group vertex.
*
* @param stage
* the stage this group vertex belongs to
* @param id
* the ID of the management group vertex
* @param name
* the name of the new management group vertex
*/
public ManagementGroupVertex(final ManagementStage stage, final ManagementGroupVertexID id, final String name) {
this.stage = stage;
this.id = id;
this.name = name;
stage.addGroupVertex(this);
}
/**
* Returns the ID of this management group vertex.
*
* @return the ID of this management group vertex
*/
public ManagementGroupVertexID getID() {
return this.id;
}
/**
* Returns the name of this management group vertex.
*
* @return the anme of this management group vertex, possibly <code>null</code>
*/
public String getName() {
return name;
}
/**
* Inserts a new edge starting at this group vertex at the given index.
*
* @param edge
* the edge to be added
* @param index
* the index at which the edge shall be added
*/
void insertForwardEdge(final ManagementGroupEdge edge, final int index) {
while (index >= this.forwardEdges.size()) {
this.forwardEdges.add(null);
}
this.forwardEdges.set(index, edge);
}
/**
* Inserts a new edge arriving at this group vertex at the given index.
*
* @param edge
* the edge to be added
* @param index
* the index at which the edge shall be added
*/
void insertBackwardEdge(final ManagementGroupEdge edge, final int index) {
while (index >= this.backwardEdges.size()) {
this.backwardEdges.add(null);
}
this.backwardEdges.set(index, edge);
}
/**
* Returns the number of edges originating at this group vertex.
*
* @return the number of edges originating at this group vertex
*/
public int getNumberOfForwardEdges() {
return this.forwardEdges.size();
}
/**
* Returns the number of edges arriving at this group vertex.
*
* @return the number of edges arriving at this group vertex
*/
public int getNumberOfBackwardEdges() {
return this.backwardEdges.size();
}
/**
* Returns the group edge which leaves this group vertex at the given index.
*
* @param index
* the index of the group edge
* @return the group edge which leaves this group vertex at the given index or <code>null</code> if no such group
* edge exists
*/
public ManagementGroupEdge getForwardEdge(final int index) {
if (index < this.forwardEdges.size()) {
return this.forwardEdges.get(index);
}
return null;
}
/**
* Returns the group edge which arrives at this group vertex at the given index.
*
* @param index
* the index of the group edge
* @return the group edge which arrives at this group vertex at the given index or <code>null</code> if no such
* group edge exists
*/
public ManagementGroupEdge getBackwardEdge(final int index) {
if (index < this.backwardEdges.size()) {
return this.backwardEdges.get(index);
}
return null;
}
/**
* Adds a {@link ManagementVertex} to this group vertex.
*
* @param vertex
* the vertex to be added
*/
void addGroupMember(final ManagementVertex vertex) {
while (this.groupMembers.size() <= vertex.getIndexInGroup()) {
this.groupMembers.add(null);
}
this.groupMembers.set(vertex.getIndexInGroup(), vertex);
this.getGraph().addVertex(vertex.getID(), vertex);
}
/**
* Adds all management vertices which are included in this group vertex to the given list.
*
* @param vertices
* the list to which the vertices shall be added
*/
void collectVertices(final List<ManagementVertex> vertices) {
final Iterator<ManagementVertex> it = this.groupMembers.iterator();
while (it.hasNext()) {
vertices.add(it.next());
}
}
/**
* Returns the stage this management group vertex belongs to.
*
* @return the stage this management group vertex belongs to
*/
public ManagementStage getStage() {
return this.stage;
}
/**
* Returns the management graph this group vertex is part of.
*
* @return the management graph this group vertex is part of
*/
public ManagementGraph getGraph() {
return this.stage.getGraph();
}
/**
* Returns the number of management vertices included in this group vertex.
*
* @return the number of management vertices included in this group vertex
*/
public int getNumberOfGroupMembers() {
return this.groupMembers.size();
}
/**
* Returns the management vertex with the given index.
*
* @param index
* the index of the management vertex to be returned
* @return the management vertex with the given index or <code>null</code> if no such vertex exists
*/
public ManagementVertex getGroupMember(final int index) {
if (index < this.groupMembers.size()) {
return this.groupMembers.get(index);
}
return null;
}
/**
* Returns alle management vertices containted in this group
*
* @return the management vertex with the given index or <code>null</code> if no such vertex exists
*/
public List<ManagementVertex> getGroupMembers() {
return this.groupMembers;
}
/**
* 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() {
if (this.backwardEdges.size() == 0) {
return true;
}
final Iterator<ManagementGroupEdge> it = this.backwardEdges.iterator();
while (it.hasNext()) {
if (it.next().getSource().getStageNumber() == this.getStageNumber()) {
return false;
}
}
return true;
}
/**
* 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() {
if (this.forwardEdges.size() == 0) {
return true;
}
final Iterator<ManagementGroupEdge> it = this.forwardEdges.iterator();
while (it.hasNext()) {
if (it.next().getTarget().getStageNumber() == this.getStageNumber()) {
return false;
}
}
return true;
}
/**
* Returns the number of the management stage this group vertex belongs to.
*
* @return the number of the management stage this group vertex belongs to
*/
public int getStageNumber() {
return this.stage.getStageNumber();
}
@Override
public void read(final DataInput in) throws IOException {
int numberOfForwardLinks = in.readInt();
for (int i = 0; i < numberOfForwardLinks; i++) {
final ManagementGroupVertexID targetGroupVertexID = new ManagementGroupVertexID();
targetGroupVertexID.read(in);
final ManagementGroupVertex targetGroupVertex = getGraph().getGroupVertexByID(targetGroupVertexID);
final int sourceIndex = in.readInt();
final int targetIndex = in.readInt();
final ChannelType channelType = EnumUtils.readEnum(in, ChannelType.class);
new ManagementGroupEdge(this, sourceIndex, targetGroupVertex, targetIndex, channelType);
}
}
@Override
public void write(final DataOutput out) throws IOException {
// Write the number of forward links
out.writeInt(this.forwardEdges.size());
final Iterator<ManagementGroupEdge> it = this.forwardEdges.iterator();
while (it.hasNext()) {
final ManagementGroupEdge groupEdge = it.next();
groupEdge.getTarget().getID().write(out);
out.writeInt(groupEdge.getSourceIndex());
out.writeInt(groupEdge.getTargetIndex());
EnumUtils.writeEnum(out, groupEdge.getChannelType());
}
}
/**
* Returns the list of successors of this group vertex. A successor is a group vertex which can be reached via a
* group edge originating at this group vertex.
*
* @return the list of successors of this group vertex.
*/
public List<ManagementGroupVertex> getSuccessors() {
final List<ManagementGroupVertex> successors = new ArrayList<ManagementGroupVertex>();
for (ManagementGroupEdge edge : this.forwardEdges) {
successors.add(edge.getTarget());
}
return successors;
}
/**
* Returns the list of predecessors of this group vertex. A predecessors is a group vertex which can be reached via
* a group edge arriving at this group vertex.
*
* @return the list of predecessors of this group vertex.
*/
public List<ManagementGroupVertex> getPredecessors() {
final List<ManagementGroupVertex> predecessors = new ArrayList<ManagementGroupVertex>();
for (ManagementGroupEdge edge : this.backwardEdges) {
predecessors.add(edge.getSource());
}
return predecessors;
}
@Override
public String toString() {
return String.format("ManagementGroupVertex(%s)", getName());
}
/**
* Returns Json representation of this ManagementGroupVertex
*
* @return
*/
public String toJson() {
StringBuilder json = new StringBuilder("");
json.append("{");
json.append("\"groupvertexid\": \"" + this.getID() + "\",");
json.append("\"groupvertexname\": \"" + StringUtils.escapeHtml(this.getName()) + "\",");
json.append("\"numberofgroupmembers\": " + this.getNumberOfGroupMembers() + ",");
json.append("\"groupmembers\": [");
// Count state status of group members
Map<ExecutionState, Integer> stateCounts = new HashMap<ExecutionState, Integer>();
// initialize with 0
for (ExecutionState state : ExecutionState.values()) {
stateCounts.put(state, new Integer(0));
}
for(int j = 0; j < this.getNumberOfGroupMembers(); j++) {
ManagementVertex vertex = this.getGroupMember(j);
json.append(vertex.toJson());
// print delimiter
if(j != this.getNumberOfGroupMembers() - 1) {
json.append(",");
}
// Increment state status count
Integer count = stateCounts.get(vertex.getExecutionState()) + new Integer(1);
stateCounts.put(vertex.getExecutionState(), count);
}
json.append("],");
json.append("\"backwardEdges\": [");
for(int edgeIndex = 0; edgeIndex < this.getNumberOfBackwardEdges(); edgeIndex++) {
ManagementGroupEdge edge = this.getBackwardEdge(edgeIndex);
json.append("{");
json.append("\"groupvertexid\": \"" + edge.getSource().getID() + "\",");
json.append("\"groupvertexname\": \"" + StringUtils.escapeHtml(edge.getSource().getName()) + "\",");
json.append("\"channelType\": \"" + edge.getChannelType() + "\"");
json.append("}");
// print delimiter
if(edgeIndex != this.getNumberOfBackwardEdges() - 1) {
json.append(",");
}
}
json.append("]");
// list number of members for each status
for (Map.Entry<ExecutionState, Integer> stateCount : stateCounts.entrySet()) {
json.append(",\""+stateCount.getKey()+"\": " + stateCount.getValue());
}
json.append("}");
return json.toString();
}
}