/***************************************************************************
* Copyright (c) 2013 VMware, Inc. All Rights Reserved.
* 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.
***************************************************************************/
/***************************************************************************
* Copyright (c) 2012 VMware, Inc. All Rights Reserved.
* 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.vmware.vhadoop.util;
import java.util.ArrayList;
import java.util.List;
import com.vmware.vhadoop.util.CompoundStatus.TaskStatus.TaskState;
/**
* CompoundStatus is a class used to allow a method to register successes or failures in its operation
* and then return the status to the caller which can then combine it with its own status.
* In this way, an audit trail of statuses can be build up over a call stack and can then finally be
* analyzed by the initial caller which decides what to do about the results
* Note that the caller should not assume that every successful method in the stack above will
* necessarily have called registerTaskSuccess(). This is why the final focus is on how many failures there were.
* Each CompoundStatus has a name and each TaskStatus created within it gets tagged with that name
* Hence when compoundStatus objects are combined with each other, it is clear which operation registered what.
* "Fatal" here means that the entire operation should be subsequently aborted - there is no point continuing
*
* @author bcorrie
*
*/
public class CompoundStatus {
private List<TaskStatus> _taskStatusList; /* Ordered by timestamp for audit */
private String _name;
public static class TaskStatus {
public enum TaskState{SUCCEEDED, INCOMPLETE, FAILED};
private String _compoundName;
private final long _timeOccurred = System.currentTimeMillis();
private TaskState _taskState;
private boolean _isFatal;
private String _message;
private Integer _errorCode;
public TaskStatus(TaskState state, boolean isFatal, String message, String compoundName, Integer errorCode) {
_taskState = state;
_isFatal = isFatal;
_message = message;
_compoundName = compoundName;
_errorCode = errorCode;
}
public TaskState getTaskState() {
return _taskState;
}
public boolean getIsFatal() {
return _isFatal;
}
public String getMessage() {
return _message;
}
public long getTimeOccurred() {
return _timeOccurred;
}
public String getCompoundName() {
return _compoundName;
}
public Integer getErrorCode() {
return _errorCode;
}
@Override
public String toString() {
return _timeOccurred+": "+_compoundName+" -> "+_message;
}
}
/* Create a new compound status. The name is typically the class/method name that created the status */
public CompoundStatus(String name) {
_taskStatusList = new ArrayList<TaskStatus>();
_name = name;
}
/* Incorporate the input status into this status */
public void addStatus(CompoundStatus statusToAdd) {
_taskStatusList.addAll(statusToAdd._taskStatusList);
}
/* If a task succeeds, provide a record of this */
public void registerTaskSucceeded() {
_taskStatusList.add(new TaskStatus(TaskState.SUCCEEDED, false, null, _name, null));
}
public void registerTaskFailed(boolean isFatal, String errorMsg) {
registerTaskFailed(isFatal, errorMsg, null);
}
/* If a task fails, record the failure. It could be fatal or not */
public void registerTaskFailed(boolean isFatal, String errorMsg, Integer errorCode) {
_taskStatusList.add(new TaskStatus(TaskState.FAILED, false, errorMsg, _name, errorCode));
}
public void registerTaskIncomplete(boolean isFatal, String errorMsg) {
registerTaskIncomplete(isFatal, errorMsg, null);
}
/* If a task doesn't complete, this could be indication for a retry */
public void registerTaskIncomplete(boolean isFatal, String errorMsg, Integer errorCode) {
_taskStatusList.add(new TaskStatus(TaskState.INCOMPLETE, false, errorMsg, _name, errorCode));
}
public int getTotalTaskCount() {
return _taskStatusList.size();
}
private int getTaskCountForState(TaskState state) {
int result = 0;
for (TaskStatus status : _taskStatusList) {
if (status.getTaskState().equals(state)) {
result++;
}
}
return result;
}
public int getFailedTaskCount() {
return getTaskCountForState(TaskState.FAILED);
}
public int getIncompleteTaskCount() {
return getTaskCountForState(TaskState.INCOMPLETE);
}
public int getFatalFailureCount() {
int result = 0;
for (TaskStatus status : _taskStatusList) {
if (status.getIsFatal()) {
result++;
}
}
return result;
}
public TaskStatus getFirstFailure() {
return getFirstFailure(null);
}
public TaskStatus getFirstFailure(String failedCompoundName) {
TaskStatus result = null;
for (TaskStatus taskStatus : _taskStatusList) {
if (((failedCompoundName == null) || taskStatus._compoundName.equals(failedCompoundName)) &&
!taskStatus._taskState.equals(TaskState.SUCCEEDED)) {
if ((result == null) || result._timeOccurred > taskStatus._timeOccurred) {
result = taskStatus;
}
}
}
return result;
}
/* true == no failure found */
public boolean screenStatusesForSpecificFailures(String[] failedCompoundNames) {
for (TaskStatus taskStatus : _taskStatusList) {
for (String failureName : failedCompoundNames) {
if (taskStatus._compoundName.equals(failureName) &&
!taskStatus._taskState.equals(TaskState.SUCCEEDED)) {
return false;
}
}
}
return true;
}
}