Skip to content

Commit

Permalink
Document pieces of the activity framework
Browse files Browse the repository at this point in the history
This is needed before introducing simulations support: state
of the simulation (OID of the current simulation result),
calls to SimulationResultManager, and so on.
  • Loading branch information
mederly committed Dec 8, 2022
1 parent 55a5570 commit 0dc08ac
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@

import static com.evolveum.midpoint.util.MiscUtil.stateCheck;

/**
* Object identifying an activity in the (logical) activity tree.
*
* For example, if we have a composite activity of two reconciliations (`recon-A` and `recon-B`), there are the following
* activity paths present:
*
* - `reconA/operationCompletion`
* - `reconA/resourceObjects`
* - `reconA/remainingShadows`
* - `reconB/operationCompletion`
* - `reconB/resourceObjects`
* - `reconB/remainingShadows`
*
* These paths denote the activities _regardless_ of whether they are distributed (into worker tasks), delegated (into subtasks),
* or both.
*
* Empty path denotes the root activity.
*
* Externalized form is {@link ActivityPathType}.
*/
public class ActivityPath implements Serializable {

/** Unmodifiable list of activity identifiers. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private void createEmptyAggregatedDataObject(
"resolve report ref",
runningTask,
result);
ReportDataType reportData = new ReportDataType(commonTaskBeans.prismContext)
ReportDataType reportData = new ReportDataType()
.name(SaveReportFileSupport.getNameOfExportedReportData(report, getType(report)));
String oid = commonTaskBeans.repositoryService.addObject(reportData.asPrismObject(), null, result);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.evolveum.midpoint.repo.common.activity.run.DelegatingActivityRun;
import com.evolveum.midpoint.repo.common.activity.run.DistributingActivityRun;
import com.evolveum.midpoint.repo.common.activity.run.ActivityRunInstantiationContext;
import com.evolveum.midpoint.repo.common.activity.run.state.ActivityState;
import com.evolveum.midpoint.repo.common.activity.run.state.ActivityStateDefinition;
import com.evolveum.midpoint.repo.common.activity.run.task.ActivityBasedTaskRun;
import com.evolveum.midpoint.schema.util.task.ActivityPath;
Expand All @@ -40,35 +41,47 @@
/**
* Binds together all the information about an activity and its run (if present).
*
* The {@link Activity} itself is a binder for (shortly speaking):
*
* . ({@link #definition}) {@link ActivityDefinition}
* . ({@link #run}) {@link AbstractActivityRun} (optional - only if we are dealing with the currently executing activity)
* * {@link ActivityState};
* . ({@link #tree}) {@link ActivityTree}
* * {@link ActivityTreeStateOverview}
* . {@link ActivityHandler}
*
* @param <WD> Type of the work definition object
* @param <AH> Type of the activity handler object
*/
public abstract class Activity<WD extends WorkDefinition, AH extends ActivityHandler<WD, AH>>
implements DebugDumpable {

private static final @NotNull ItemPath TASK_ROLE_PATH = ItemPath.create(TaskType.F_ACTIVITY_STATE, TaskActivityStateType.F_TASK_ROLE);
private static final @NotNull ItemPath TASK_ROLE_PATH =
ItemPath.create(TaskType.F_ACTIVITY_STATE, TaskActivityStateType.F_TASK_ROLE);

/**
* Identifier of the activity. It is unique at the current level in the activity tree.
* May be defined in {@link ActivityDefinition#explicitlyDefinedIdentifier}, but usually it is generated
* when the list of sub-activities is finalized.
*
* See {@link #setupIdentifiers(List)}.
*/
private String identifier;

/**
* Definition of the activity.
*/
/** Definition of the activity. Parsed from XML of the activity and from the legacy task extension. */
@NotNull private final ActivityDefinition<WD> definition;

/**
* Run of the activity. May be null.
*/
/** Run of the activity. May be null. The current activity state is here. */
private AbstractActivityRun<WD, AH, ?> run;

/**
* Reference to the tree object.
*/
/** Reference to the tree object. The tree state is here. */
@NotNull private final ActivityTree tree;

/** TODO */
/**
* Is this activity the local root i.e. top-level activity running in the current task?
* If the task is a root task, this implies the activity is the global root of the activity tree.
* But if the task is a subtask, the activity may be a sub-activity.
*/
private boolean localRoot;

/**
Expand All @@ -78,10 +91,13 @@ public abstract class Activity<WD extends WorkDefinition, AH extends ActivityHan
*/
@NotNull private final Map<String, Activity<?, ?>> childrenMap = Collections.synchronizedMap(new LinkedHashMap<>());

/** Was {@link #childrenMap} already initialized? TODO move to a special class with the map. */
private boolean childrenMapInitialized;

/** (Global) path of this activity in the activity tree. */
@NotNull private final Lazy<ActivityPath> pathLazy = Lazy.from(this::computePath);

/** (Local) path of this activity in the current task. */
@NotNull private final Lazy<ActivityPath> localPathLazy = Lazy.from(this::computeLocalPath);

Activity(@NotNull ActivityDefinition<WD> definition, @NotNull ActivityTree tree) {
Expand All @@ -93,17 +109,11 @@ public String getIdentifier() {
return identifier;
}

public void setIdentifier(String identifier) {
this.identifier = identifier;
}

@NotNull
public ActivityDefinition<WD> getDefinition() {
public @NotNull ActivityDefinition<WD> getDefinition() {
return definition;
}

@NotNull
public WD getWorkDefinition() {
public @NotNull WD getWorkDefinition() {
return definition.getWorkDefinition();
}

Expand All @@ -119,28 +129,31 @@ public WD getWorkDefinition() {
return definition.getControlFlowDefinition();
}

@NotNull
public abstract AH getHandler();
public abstract @NotNull AH getHandler();

@NotNull
protected abstract ActivityRunSupplier<WD, AH> getLocalRunSupplier();
/**
* Returns objects that create {@link AbstractActivityRun} objects for this activity.
* It is used in cases where the activity runs locally i.e. is not delegated nor distributed.
*
* See {@link ActivityRunSupplier}.
*/
protected abstract @NotNull ActivityRunSupplier<WD, AH> getLocalRunSupplier();

@NotNull
protected abstract CandidateIdentifierFormatter getCandidateIdentifierFormatter();
/** Returns objects that suggest child activity identifiers. */
protected abstract @NotNull CandidateIdentifierFormatter getCandidateIdentifierFormatter();

public abstract @NotNull ActivityStateDefinition<?> getActivityStateDefinition();

public AbstractActivityRun<WD, AH, ?> getRun() {
return run;
}

@NotNull
public ActivityTree getTree() {
public @NotNull ActivityTree getTree() {
return tree;
}

public void setLocalRoot(boolean localRoot) {
this.localRoot = localRoot;
public void setLocalRoot() {
this.localRoot = true;
}

public abstract Activity<?, ?> getParent();
Expand Down Expand Up @@ -179,14 +192,14 @@ public String debugDump(int indent) {
return sb.toString();
}

@NotNull
private String getDebugDumpLabel() {
private @NotNull String getDebugDumpLabel() {
return getClass().getSimpleName() + " [identifier '" + identifier + "']" +
(isRoot() ? " (root)" : "") +
(isLocalRoot() ? " (local root)" : "");
}

public AbstractActivityRun<?, ?, ?> createRun(ActivityBasedTaskRun taskRun, OperationResult result) {
/** Creates and sets the activity run ({@link #run}). */
public @NotNull AbstractActivityRun<?, ?, ?> createRun(ActivityBasedTaskRun taskRun, OperationResult result) {
stateCheck(run == null, "Run is already created in %s", this);
ActivityRunInstantiationContext<WD, AH> context = new ActivityRunInstantiationContext<>(this, taskRun);
RunType runType = determineRunType(taskRun.getRunningTask());
Expand Down Expand Up @@ -247,15 +260,15 @@ private TaskRoleType getRoleOfTask(Task activityTask) {
return activityTask.getPropertyRealValue(TASK_ROLE_PATH, TaskRoleType.class);
}

@NotNull
public Activity<?, ?> getChild(String identifier) throws SchemaException {
public @NotNull Activity<?, ?> getChild(String identifier) throws SchemaException {
initializeChildrenMapIfNeeded();
Activity<?, ?> child = childrenMap.get(identifier);
if (child != null) {
return child;
} else {
throw new IllegalArgumentException("Child with identifier " + identifier + " was not found among children of "
+ this + ". Known children are: " + getChildrenMapCopy().keySet());
throw new IllegalArgumentException(
String.format("Child with identifier %s was not found among children of %s. Known children are: %s",
identifier, this, getChildrenMapCopy().keySet()));
}
}

Expand Down Expand Up @@ -337,13 +350,12 @@ private void tailorChildren(ArrayList<Activity<?, ?>> childrenList) throws Schem
.execute();
}

/** Is this activity the (global) root of the activity tree? */
public boolean isRoot() {
return getParent() == null;
}

/**
* Is this activity the local root i.e. root of run in the current task?
*/
/** Is this activity the local root i.e. root of run in the current task? */
public boolean isLocalRoot() {
return localRoot;
}
Expand All @@ -355,13 +367,11 @@ public String toString() {
'}';
}

@NotNull
public ActivityPath getPath() {
public @NotNull ActivityPath getPath() {
return pathLazy.get();
}

@NotNull
private ActivityPath computePath() {
private @NotNull ActivityPath computePath() {
LinkedList<String> identifiers = new LinkedList<>();
Activity<?, ?> current = this;
while (!current.isRoot()) {
Expand All @@ -371,13 +381,11 @@ private ActivityPath computePath() {
return ActivityPath.fromList(identifiers);
}

@Nullable
public ActivityPath getLocalPath() {
public @Nullable ActivityPath getLocalPath() {
return localPathLazy.get();
}

@Nullable
private ActivityPath computeLocalPath() {
private @Nullable ActivityPath computeLocalPath() {
LinkedList<String> identifiers = new LinkedList<>();
Activity<?, ?> current = this;
while (!current.isLocalRoot()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@

import org.jetbrains.annotations.NotNull;

/**
* Creates (typed) {@link AbstractActivityRun} objects for given activity.
*
* - For standalone activities it is usually the {@link ActivityHandler} itself.
* - For embedded activities it is usually a custom piece of code used when defining a child activity.
*/
@FunctionalInterface
public interface ActivityRunSupplier<WD extends WorkDefinition, AH extends ActivityHandler<WD, AH>> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@

/**
* Represents the tree of activities that comprise a logical task.
*
* Basically, binds together the root activity ({@link StandaloneActivity}) and the dynamic object representing
* the state of the whole tree ({@link ActivityTreeStateOverview}).
*/
public class ActivityTree implements DebugDumpable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@

import javax.xml.datatype.XMLGregorianCalendar;

/**
* Represents the activity tree state overview that is stored in the root task.
*
* This class does _not_ hold the state itself. Instead, it contains methods that update the state in the task.
* See e.g. {@link #recordLocalRunStart(LocalActivityRun, OperationResult)} and its brethren.
*/
public class ActivityTreeStateOverview {

private static final Trace LOGGER = TraceManager.getTrace(ActivityTreeStateOverview.class);
Expand Down Expand Up @@ -276,8 +282,9 @@ private boolean isBefore(@NotNull BucketProgressOverviewType bucket1, @Nullable
}

/** Assumes that the activity run is still in progress. (I.e. also clear the "stalled since" flag.) */
public void updateItemProgressIfTimePassed(@NotNull LocalActivityRun<?, ?, ?> run, long interval,
OperationResult result) throws SchemaException, ObjectNotFoundException {
public void updateItemProgressIfTimePassed(
@NotNull LocalActivityRun<?, ?, ?> run, long interval, OperationResult result)
throws SchemaException, ObjectNotFoundException {

if (!run.shouldUpdateProgressInStateOverview() || !run.isProgressSupported()) {
LOGGER.trace("Item progress update skipped in {}", run);
Expand Down Expand Up @@ -334,7 +341,7 @@ public ActivityTreeRealizationStateType getRealizationState() {
/**
* Updates the realization state (including writing to the repository).
*/
public void updateRealizationState(ActivityTreeRealizationStateType value, OperationResult result)
void updateRealizationState(ActivityTreeRealizationStateType value, OperationResult result)
throws ActivityRunException {
try {
rootTask.setItemRealValues(PATH_REALIZATION_STATE, value);
Expand All @@ -344,11 +351,11 @@ public void updateRealizationState(ActivityTreeRealizationStateType value, Opera
}
}

public ActivityStateOverviewType getActivityStateTree() {
private ActivityStateOverviewType getActivityStateTree() {
return rootTask.getPropertyRealValueOrClone(PATH_ACTIVITY_STATE_TREE, ActivityStateOverviewType.class);
}

public void purge(OperationResult result) throws ActivityRunException {
void purge(OperationResult result) throws ActivityRunException {
updateActivityStateTree(
purgeStateRecursively(
getActivityStateTree()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
@FunctionalInterface
public interface CandidateIdentifierFormatter {

@NotNull
String formatCandidateIdentifier(int iteration);
/** Suggests child activity identifier */
@NotNull String formatCandidateIdentifier(int iteration);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,42 @@
import com.evolveum.midpoint.repo.common.activity.definition.WorkDefinition;
import com.evolveum.midpoint.repo.common.activity.handlers.ActivityHandler;

/**
* A sub-activity that does not have its own (explicit) definition. Typical examples are sub-activities of reconciliation,
* cleanup, or focus validity scan activity.
*/
public class EmbeddedActivity<WD extends WorkDefinition, AH extends ActivityHandler<WD, AH>>
extends Activity<WD, AH> {

/**
* Supplier for the run objects.
* Supplier for the run objects. Usually a simple closure pointing to appropriate constructor.
*/
@NotNull private final ActivityRunSupplier<WD, AH> activityRunSupplier;

/** TODO better name */
/**
* A piece of code that is to be executed _before_ this activity is executed.
*
* Currently executed for both local and distributing runs. (Not for delegating ones.)
*
* TODO better name, better explanation/reasoning - it is quite a hack now
*/
@Nullable private final PreRunnable<WD, AH> preRunnable;

/** TODO */
/** Suggests identifier for this activity (when generating). */
@NotNull private final CandidateIdentifierFormatter candidateIdentifierFormatter;

/** TODO */
/** Defines the activity state, e.g. its data type and persistence characteristics. */
@NotNull private final ActivityStateDefinition<?> activityStateDefinition;

@NotNull private final Activity<WD, AH> parent;

private EmbeddedActivity(@NotNull ActivityDefinition<WD> definition, @NotNull ActivityRunSupplier<WD, AH> activityRunSupplier,
@Nullable PreRunnable<WD, AH> preRunnable, @NotNull CandidateIdentifierFormatter candidateIdentifierFormatter,
@NotNull ActivityStateDefinition<?> activityStateDefinition, @NotNull ActivityTree tree,
private EmbeddedActivity(
@NotNull ActivityDefinition<WD> definition,
@NotNull ActivityRunSupplier<WD, AH> activityRunSupplier,
@Nullable PreRunnable<WD, AH> preRunnable,
@NotNull CandidateIdentifierFormatter candidateIdentifierFormatter,
@NotNull ActivityStateDefinition<?> activityStateDefinition,
@NotNull ActivityTree tree,
@NotNull Activity<WD, AH> parent) {
super(definition, tree);
this.activityRunSupplier = activityRunSupplier;
Expand Down

0 comments on commit 0dc08ac

Please sign in to comment.