/*
* Copyright (c) 2010-2017 Evolveum
*
* 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.evolveum.midpoint.task.api;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.schema.statistics.ProvisioningOperation;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import javax.xml.namespace.QName;
import java.util.Date;
/**
* TODO better name (ProgressReporter ? StatisticsReporter ? ...)
*
* Used to report state, progress and performance statistics to upper layers.
* Generally a Task is the place where such information are reported and collected.
*
* However, because of a complex nature of some operations (namely, search) it tries
* to remember the state of an operation.
*
* TODO maybe this could be simplified in the future.
*
* @author Pavol Mederly
*/
public class StateReporter {
private static final Trace LOGGER = TraceManager.getTrace(StateReporter.class);
private Task task;
private String resourceOid;
private String resourceName; // lazily set when available
public StateReporter() {
}
public StateReporter(String resourceOid, Task task) {
this.resourceOid = resourceOid;
this.task = task;
}
private String getResourceName() {
if (resourceName != null) {
return resourceName;
} else {
return resourceOid;
}
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
// Operational information
private ProvisioningOperation lastOperation = null;
private ObjectClassComplexTypeDefinition lastObjectClass = null;
private Date lastStarted = null;
public void recordIcfOperationStart(ProvisioningOperation operation, ObjectClassComplexTypeDefinition objectClassDef, String identifier) {
if (lastOperation != null) {
LOGGER.warn("Unfinished operation: {}, resource: {}, OC: {}, started: {}", lastOperation, getResourceName(), lastObjectClass, lastStarted);
}
lastOperation = operation;
lastObjectClass = objectClassDef;
lastStarted = new Date();
String object = "";
if (identifier != null) {
object = " " + identifier;
}
recordState("Starting " + operation + " of " + getObjectClassName(objectClassDef) + object + " on " + getResourceName());
}
// we just add duration, not count (we'll do this on end)
public void recordIcfOperationSuspend(ProvisioningOperation operation, ObjectClassComplexTypeDefinition objectClassDef) {
if (lastOperation != operation) {
LOGGER.warn("Suspending operation other than current: finishing {}, last recorded {}",
operation, lastOperation);
} else if (lastObjectClass == null || !lastObjectClass.getTypeName().equals(objectClassDef.getTypeName())) {
LOGGER.warn("Suspending operation on object class other than current: finishing on {}, last recorded {}",
objectClassDef.getTypeName(), lastObjectClass != null ? lastObjectClass.getTypeName() : "(null)");
} else {
long duration = System.currentTimeMillis() - lastStarted.getTime();
if (task != null) {
task.recordProvisioningOperation(resourceOid, getResourceName(), objectClassDef.getTypeName(), lastOperation, true, 0, duration);
} else {
reportNoTask(resourceOid, lastOperation);
}
}
lastOperation = null;
recordState("Returned from " + operation + " of " + objectClassDef.getTypeName().getLocalPart() + " on " + getResourceName());
}
public void recordIcfOperationResume(ProvisioningOperation operation, ObjectClassComplexTypeDefinition objectClassDef) {
if (lastOperation != null) {
LOGGER.warn("Unfinished operation: {}, resource: {}, OC: {}, started: {}", lastOperation, getResourceName(), lastObjectClass, lastStarted);
}
lastOperation = operation;
lastObjectClass = objectClassDef;
lastStarted = new Date();
recordState("Continuing " + operation + " of " + objectClassDef.getTypeName().getLocalPart() + " on " + getResourceName());
}
private String getObjectClassName(ObjectClassComplexTypeDefinition objectClassDef) {
return objectClassDef != null && objectClassDef.getTypeName() != null ? objectClassDef.getTypeName().getLocalPart() : "(null)";
}
private QName getObjectClassQName(ObjectClassComplexTypeDefinition objectClassDef) {
return objectClassDef != null ? objectClassDef.getTypeName() : null;
}
public void recordIcfOperationEnd(ProvisioningOperation operation, ObjectClassComplexTypeDefinition objectClassDef, Throwable ex, String identifier) {
long duration = -1L;
if (lastOperation != operation) {
LOGGER.warn("Finishing operation other than current: finishing {}, last recorded {}",
operation, lastOperation);
} else if (objectClassDef != null && (lastObjectClass == null || !lastObjectClass.getTypeName().equals(objectClassDef.getTypeName()))) {
LOGGER.warn("Finishing operation on object class other than current: finishing on {}, last recorded {}",
getObjectClassName(objectClassDef), getObjectClassName(lastObjectClass));
} else {
duration = System.currentTimeMillis() - lastStarted.getTime();
}
String finished;
if (ex == null) {
finished = "Successfully finished";
} else {
finished = "Finished (unsuccessfully)";
}
String durationString;
if (duration >= 0) {
durationString = " in " + duration + " ms";
} else {
durationString = "";
}
String object = "";
if (identifier != null) {
object = " " + identifier;
}
final String stateMessage =
finished + " " + operation + " of " + getObjectClassName(objectClassDef) + object + " on " + getResourceName() + durationString;
recordState(stateMessage);
if (task != null) {
if (duration >= 0) {
task.recordProvisioningOperation(resourceOid, getResourceName(), getObjectClassQName(objectClassDef), lastOperation, ex == null, 1, duration);
} else {
LOGGER.warn("Negative duration while recording provisiong operation: {}", stateMessage);
}
} else {
reportNoTask(resourceOid, lastOperation);
}
lastOperation = null;
}
private void reportNoTask(String resourceOid, ProvisioningOperation operation) {
LOGGER.warn("Couldn't report execution of ICF operation {} on resource {} because there is no task assigned.", operation, resourceOid);
}
private void recordState(String message) {
if (task != null) {
task.recordState(message);
}
}
public void setTask(Task task) {
this.task = task;
}
public Task getTask() {
return task;
}
public String getResourceOid() {
return resourceOid;
}
public void setResourceOid(String resourceOid) {
this.resourceOid = resourceOid;
}
}