All Downloads are FREE. Search and download functionalities are using the official Maven repository.

li.strolch.execution.EventBasedExecutionHandler Maven / Gradle / Ivy

package li.strolch.execution;

import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import li.strolch.agent.api.ComponentContainer;
import li.strolch.execution.command.*;
import li.strolch.execution.policy.ActivityArchivalPolicy;
import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.handler.operationslog.LogMessage;
import li.strolch.handler.operationslog.LogSeverity;
import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator;
import li.strolch.model.State;
import li.strolch.model.activity.Action;
import li.strolch.model.activity.Activity;
import li.strolch.model.activity.IActivityElement;
import li.strolch.model.policy.PolicyDef;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.policy.PolicyHandler;
import li.strolch.privilege.model.PrivilegeContext;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.utils.collections.MapOfSets;
import li.strolch.utils.dbc.DBC;

/**
 * The event based execution handler waits for events in that the {@link ExecutionPolicy} implementations must call the
 * relevant methods when the work is complete. Afterwards the next {@link Action} in the procedure is executed
 *
 * @author Robert von Burg 
 */
public class EventBasedExecutionHandler extends ExecutionHandler {

	private static final String KEY_DEFAULT_ACTIVITY_ARCHIVAL = "key:DefaultActivityArchival";
	private static final String PROP_RESTART_EXECUTION = "restartExecution";

	private MapOfSets registeredActivities;

	private DelayedExecutionTimer delayedExecutionTimer;

	public EventBasedExecutionHandler(ComponentContainer container, String componentName) {
		super(container, componentName);
	}

	@Override
	public void initialize(ComponentConfiguration configuration) throws Exception {

		this.registeredActivities = new MapOfSets<>();

		super.initialize(configuration);
	}

	@Override
	public void start() throws Exception {

		this.delayedExecutionTimer = new SimpleDurationExecutionTimer(getContainer().getAgent());

		// restart execution of activities already in execution
		if (!getConfiguration().getBoolean(PROP_RESTART_EXECUTION, Boolean.FALSE)) {
			logger.info("Not restarting execution of activities.");
		} else {
			logger.info("Restarting execution of activities.");
			runAsAgent(this::restartActivityExecution);
		}

		super.start();
	}

	@Override
	public void stop() throws Exception {

		if (this.delayedExecutionTimer != null) {
			this.delayedExecutionTimer.destroy();
			this.delayedExecutionTimer = null;
		}

		super.stop();
	}

	@Override
	public void addForExecution(String realm, Locator activityLoc) {
		Locator rootElemLoc = activityLoc.trim(3);
		synchronized (this.registeredActivities) {
			this.registeredActivities.addElement(realm, rootElemLoc);
		}
		toExecution(realm, activityLoc);
	}

	@Override
	public void removeFromExecution(String realm, Locator activityLoc) {
		Locator rootElemLoc = activityLoc.trim(3);
		synchronized (this.registeredActivities) {
			this.registeredActivities.removeElement(realm, rootElemLoc);
		}
	}

	private void restartActivityExecution(PrivilegeContext ctx) {

		// iterate the realms
		for (String realmName : getContainer().getRealmNames()) {

			// open a TX for each realm
			try (StrolchTransaction tx = openTx(realmName, ctx.getCertificate())) {

				// iterate all activities
				tx.streamActivities().forEach(activity -> {

					// we only want to restart activities which were in execution
					State state = activity.getState();
					if (!state.inExecutionPhase())
						return;

					logger.info("Starting Execution of " + activity.getLocator() + " on realm " + realmName);

					// Activities need to be in state STOPPED to restart
					if (state == State.ERROR) {
						activity.getActionsWithState(State.ERROR).forEach(a -> a.setState(State.STOPPED));
					} else if (state == State.WARNING) {
						activity.getActionsWithState(State.WARNING).forEach(a -> a.setState(State.STOPPED));
					} else if (state == State.EXECUTION) {
						activity.getActionsWithState(State.EXECUTION).forEach(a -> a.setState(State.STOPPED));
					}
					tx.update(activity);

					// register for execution
					this.registeredActivities.addElement(realmName, activity.getLocator());
				});

				// commit changes to state
				tx.commitOnClose();
			}

			// trigger execution of the registered activities
			triggerExecution(realmName);
		}
	}

	@Override
	public void triggerExecution(String realm) {
		synchronized (this.registeredActivities) {
			Set locators = this.registeredActivities.getSet(realm);
			if (locators != null) {
				for (Locator locator : locators) {
					// execute async
					toExecution(realm, locator);
				}
			}
		}
	}

	@Override
	public void toExecution(String realm, Locator locator) {
		getExecutor().execute(() -> {
			try {
				runAsAgent(ctx -> {
					toExecution(realm, locator, ctx);
				});
			} catch (Exception e) {
				logger.error("Failed to set " + locator + " to execution due to " + e.getMessage(), e);

				if (getContainer().hasComponent(OperationsLog.class)) {
					getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
							ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.execution")
							.value("reason", e));
				}
			}
		});
	}

	private ExecutorService getExecutor() {
		return getExecutorService("ExecutionHandler");
	}

	@Override
	public void toExecuted(String realm, Locator locator) {
		getExecutor().execute(() -> {
			try {
				runAsAgent(ctx -> {
					toExecuted(realm, locator, ctx);
				});
			} catch (Exception e) {
				logger.error("Failed to set " + locator + " to executed due to " + e.getMessage(), e);

				if (getContainer().hasComponent(OperationsLog.class)) {
					getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
							ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed")
							.value("reason", e));
				}
			}
		});
	}

	@Override
	public void toStopped(String realm, Locator locator) {
		getExecutor().execute(() -> {
			try {
				runAsAgent(ctx -> {
					toStopped(realm, locator, ctx);
				});
			} catch (Exception e) {
				logger.error("Failed to set " + locator + " to stopped due to " + e.getMessage(), e);

				if (getContainer().hasComponent(OperationsLog.class)) {
					getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
							ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.stopped")
							.value("reason", e));
				}
			}
		});
	}

	@Override
	public void toError(String realm, Locator locator) {
		getExecutor().execute(() -> {
			try {
				runAsAgent(ctx -> {
					toError(realm, locator, ctx);
				});
			} catch (Exception e) {
				logger.error("Failed to set " + locator + " to error due to " + e.getMessage(), e);

				if (getContainer().hasComponent(OperationsLog.class)) {
					getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
							ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error")
							.value("reason", e));
				}
			}
		});
	}

	@Override
	public void toWarning(String realm, Locator locator) {
		getExecutor().execute(() -> {
			try {
				runAsAgent(ctx -> {
					toWarning(realm, locator, ctx);
				});
			} catch (Exception e) {
				logger.error("Failed to set " + locator + " to warning due to " + e.getMessage(), e);

				if (getContainer().hasComponent(OperationsLog.class)) {
					getComponent(OperationsLog.class).addMessage(new LogMessage(realm, locator, LogSeverity.EXCEPTION,
							ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning")
							.value("reason", e));
				}
			}
		});
	}

	@Override
	public void archiveActivity(String realm, Locator activityLoc) {
		getExecutor().execute(() -> {
			try {
				runAsAgent(ctx -> {
					try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), ActivityArchivalPolicy.class)) {
						tx.lock(activityLoc);

						Activity activity = tx.findElement(activityLoc);

						logger.info("Activity " + activity.getLocator() + " is in state " + activity.getState());

						PolicyDef policyDef;
						if (activity.hasPolicyDef(ActivityArchivalPolicy.class.getSimpleName())) {
							policyDef = activity.getPolicyDef(ActivityArchivalPolicy.class.getSimpleName());
						} else {
							policyDef = PolicyDef.valueOf(ActivityArchivalPolicy.class.getSimpleName(),
									KEY_DEFAULT_ACTIVITY_ARCHIVAL);
						}

						PolicyHandler policyHandler = getComponent(PolicyHandler.class);
						ActivityArchivalPolicy archivalPolicy = policyHandler.getPolicy(policyDef, tx);
						archivalPolicy.archive(activity);

						tx.commitOnClose();
					}
				});
			} catch (Exception e) {
				logger.error("Failed to archive " + activityLoc + " due to " + e.getMessage(), e);

				if (getContainer().hasComponent(OperationsLog.class)) {
					getComponent(OperationsLog.class).addMessage(
							new LogMessage(realm, activityLoc, LogSeverity.EXCEPTION,
									ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.archive")
									.value("reason", e));
				}
			}
		});
	}

	private void toExecution(String realm, Locator elementLoc, PrivilegeContext ctx) {
		try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), ExecuteActivityCommand.class)) {

			Locator activityLoc = elementLoc.trim(3);
			tx.lock(activityLoc);

			Activity activity = tx.findElement(activityLoc, true);
			if (activity == null) {
				logger.error("Element for locator " + elementLoc + " does not exist!");
				synchronized (this.registeredActivities) {
					this.registeredActivities.removeElement(realm, activityLoc);
				}
				return;
			}

			ExecuteActivityCommand command = new ExecuteActivityCommand(getContainer(), tx);
			command.setActivity(activity);
			command.doCommand();

			tx.commitOnClose();
		}
	}

	private void toExecuted(String realm, Locator actionLoc, PrivilegeContext ctx) {

		Locator activityLoc = actionLoc.trim(3);

		try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) {

			tx.lock(activityLoc);

			Action action = tx.findElement(actionLoc);

			// set this action to executed
			SetActionToExecutedCommand command = new SetActionToExecutedCommand(getContainer(), tx);
			command.setAction(action);
			command.doCommand();

			// flush so we can see that changes performed
			tx.flush();

			// if the activity is now executed, remove it from the registered activities
			Activity activity = action.getRootElement().getClone(true);
			if (activity.getState().isExecuted()) {

				synchronized (this.registeredActivities) {
					if (!this.registeredActivities.removeElement(realm, activityLoc))
						logger.warn("Activity " + activityLoc + " already removed from registered activities!");
				}

				archiveActivity(realm, activity.getLocator());

			} else {

				// otherwise execute any next action(s) for this action's activity

				ExecuteActivityCommand execCommand = new ExecuteActivityCommand(getContainer(), tx);
				execCommand.setActivity(activity);
				execCommand.doCommand();

				// flush so we can see the changes performed
				tx.flush();
			}

			tx.commitOnClose();
		}

		// now trigger a further execution of any other activities needed execution in this realm
		triggerExecution(realm);
	}

	private void toWarning(String realm, Locator actionLoc, PrivilegeContext ctx) {
		try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) {
			Locator rootElemLoc = actionLoc.trim(3);
			tx.lock(rootElemLoc);

			IActivityElement elem = tx.findElement(actionLoc);
			DBC.INTERIM.assertEquals("toWarning only for Action!", Action.class, elem.getClass());

			SetActionToWarningCommand command = new SetActionToWarningCommand(getContainer(), tx);
			command.setAction((Action) elem);
			command.doCommand();

			tx.commitOnClose();
		}
	}

	private void toError(String realm, Locator actionLoc, PrivilegeContext ctx) {
		try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToExecutedCommand.class)) {
			Locator rootElemLoc = actionLoc.trim(3);
			tx.lock(rootElemLoc);

			IActivityElement elem = tx.findElement(actionLoc);
			DBC.INTERIM.assertEquals("toError only for Action!", Action.class, elem.getClass());

			SetActionToErrorCommand command = new SetActionToErrorCommand(getContainer(), tx);
			command.setAction((Action) elem);
			command.doCommand();

			tx.commitOnClose();
		}
	}

	private void toStopped(String realm, Locator actionLoc, PrivilegeContext ctx) {
		try (StrolchTransaction tx = openTx(realm, ctx.getCertificate(), SetActionToStoppedCommand.class)) {
			Locator rootElemLoc = actionLoc.trim(3);
			tx.lock(rootElemLoc);

			IActivityElement elem = tx.findElement(actionLoc);
			DBC.INTERIM.assertEquals("toStopped only for Action!", Action.class, elem.getClass());

			SetActionToStoppedCommand command = new SetActionToStoppedCommand(getContainer(), tx);
			command.setAction((Action) elem);
			command.doCommand();

			tx.commitOnClose();
		}

		// now trigger a further execution of any other activities needed execution in this realm
		triggerExecution(realm);
	}

	@Override
	public DelayedExecutionTimer getDelayedExecutionTimer() {
		return this.delayedExecutionTimer;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy