blob: a9ab84d25d9080b2bc3973acf9ad2e0552f583c0 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.util;
import java.util.ArrayList;
/** Utility to assist with generation of progress reports. Applications build
* a hierarchy of {@link Progress} instances, each modelling a phase of
* execution. The root is constructed with {@link #Progress()}. Nodes for
* sub-phases are created by calling {@link #addPhase()}.
*/
public class Progress {
private String status = "";
private float progress;
private int currentPhase;
private ArrayList<Progress> phases = new ArrayList<Progress>();
private Progress parent;
private float progressPerPhase;
/** Creates a new root node. */
public Progress() {}
/** Adds a named node to the tree. */
public Progress addPhase(String status) {
Progress phase = addPhase();
phase.setStatus(status);
return phase;
}
/** Adds a node to the tree. */
public synchronized Progress addPhase() {
Progress phase = new Progress();
phases.add(phase);
phase.parent = this;
progressPerPhase = 1.0f / (float)phases.size();
return phase;
}
/** Called during execution to move to the next phase at this level in the
* tree. */
public synchronized void startNextPhase() {
currentPhase++;
}
/** Returns the current sub-node executing. */
public synchronized Progress phase() {
return phases.get(currentPhase);
}
/** Completes this node, moving the parent node to its next child. */
public void complete() {
// we have to traverse up to our parent, so be careful about locking.
Progress myParent;
synchronized(this) {
progress = 1.0f;
myParent = parent;
}
if (myParent != null) {
// this will synchronize on the parent, so we make sure we release
// our lock before getting the parent's, since we're traversing
// against the normal traversal direction used by get() or toString().
// We don't need transactional semantics, so we're OK doing this.
myParent.startNextPhase();
}
}
/** Called during execution on a leaf node to set its progress. */
public synchronized void set(float progress) {
this.progress = progress;
}
/** Returns the overall progress of the root. */
// this method probably does not need to be synchronized as getINternal() is synchronized
// and the node's parent never changes. Still, it doesn't hurt.
public synchronized float get() {
Progress node = this;
while (node.parent != null) { // find the root
node = parent;
}
return node.getInternal();
}
/** Computes progress in this node. */
private synchronized float getInternal() {
int phaseCount = phases.size();
if (phaseCount != 0) {
float subProgress =
currentPhase < phaseCount ? phase().getInternal() : 0.0f;
return progressPerPhase*(currentPhase + subProgress);
} else {
return progress;
}
}
public synchronized void setStatus(String status) {
this.status = status;
}
public String toString() {
StringBuffer result = new StringBuffer();
toString(result);
return result.toString();
}
private synchronized void toString(StringBuffer buffer) {
buffer.append(status);
if (phases.size() != 0 && currentPhase < phases.size()) {
buffer.append(" > ");
phase().toString(buffer);
}
}
}