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

com.github.euler.core.PipelineExecution Maven / Gradle / Ivy

package com.github.euler.core;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.SupervisorStrategy;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.Receive;
import akka.actor.typed.javadsl.ReceiveBuilder;

public class PipelineExecution extends AbstractBehavior {

    public static Behavior create(Task[] tasks) {
        return Behaviors.setup((context) -> new PipelineExecution(context, tasks));
    }

    private final Task[] tasks;
    private PipelineExecutionState state;

    private Map> mapping;
    private ActorRef responseAdapter;

    public PipelineExecution(ActorContext context, Task[] tasks) {
        super(context);
        this.tasks = tasks;
        this.state = new PipelineExecutionState();
        this.mapping = new HashMap<>();

        this.responseAdapter = context.messageAdapter(ProcessorCommand.class, InternalAdaptedProcessorCommand::new);
    }

    @Override
    public Receive createReceive() {
        ReceiveBuilder builder = newReceiveBuilder();
        builder.onMessage(JobTaskToProcess.class, this::onJobTaskToProcess);
        builder.onMessage(InternalAdaptedProcessorCommand.class, this::onInternalAdaptedProcessorCommand);
        builder.onMessage(InternalJobTaskFailed.class, this::onInternalJobTaskFailed);
        builder.onMessage(Flush.class, this::onFlush);
        return builder.build();
    }

    public Behavior onFlush(Flush msg) {
        for (Task task : tasks) {
            if (task.isFlushable() && mapping.containsKey(task.name())) {
                ActorRef taskRef = getTaskRef(task);
                taskRef.tell(msg);
            }
        }
        return Behaviors.same();
    }

    private Behavior onJobTaskToProcess(JobTaskToProcess msg) {
        state.onMessage(msg);
        sendToNextOrFinish(msg);
        return this;
    }

    private Behavior onInternalAdaptedProcessorCommand(InternalAdaptedProcessorCommand msg) {
        if (msg.processorCommand instanceof JobTaskFinished) {
            onJobTaskFinished((JobTaskFinished) msg.processorCommand);
        } else if (msg.processorCommand instanceof JobTaskFailed) {
            onJobTaskFailed((JobTaskFailed) msg.processorCommand);
        } else {
            throw new IllegalArgumentException("Impossible to handle " + msg.processorCommand.getClass().getName());
        }

        return this;
    }

    private void onJobTaskFailed(JobTaskFailed msg) {
        ActorRef replyTo = state.getReplyTo(msg.itemURI);
        ProcessingContext ctx = state.mergeContext(msg.itemURI, msg.ctx);
        replyTo.tell(new JobTaskFinished(msg.uri, msg.itemURI, ctx));
        state.finish(msg.itemURI);
    }

    private Behavior onInternalJobTaskFailed(InternalJobTaskFailed msg) {
        mapping.remove(msg.taskName);
        ActorRef replyTo = state.getReplyTo(msg.itemURI);
        ProcessingContext ctx = state.getProcessingContext(msg.itemURI);
        replyTo.tell(new JobTaskFinished(msg.uri, msg.itemURI, ctx));
        state.finish(msg.itemURI);
        return Behaviors.same();
    }

    private void onJobTaskFinished(JobTaskFinished msg) {
        ProcessingContext ctx = state.mergeContext(msg.itemURI, msg.ctx);
        sendToNextOrFinish(new JobTaskToProcess(msg.uri, msg.itemURI, ctx, responseAdapter));
    }

    private void sendToNextOrFinish(JobTaskToProcess msg) {
        Task task = getNextTask(msg);
        if (task != null) {
            ActorRef taskRef = getOrSpawnTaskRef(task, msg);
            JobTaskToProcess adaptedMsg = new JobTaskToProcess(msg.uri, msg.itemURI, msg.ctx, responseAdapter);
            taskRef.tell(adaptedMsg);
        } else {
            ActorRef replyTo = state.getReplyTo(msg.itemURI);
            replyTo.tell(new JobTaskFinished(msg, msg.ctx));
            state.finish(msg.itemURI);
        }
    }

    private Task getNextTask(JobTaskToProcess msg) {
        Task task = null;
        int position = state.getPosition(msg.itemURI);
        while (task == null && position < tasks.length) {
            if (tasks[position].accept(msg)) {
                task = tasks[position];
            }
            position++;
            state.setPosition(msg.itemURI, position);
        }
        return task;
    }

    private ActorRef getTaskRef(Task task) {
        return mapping.get(task.name());
    }

    private ActorRef getOrSpawnTaskRef(Task task, JobTaskToProcess msg) {
        ActorRef ref = mapping.computeIfAbsent(task.name(), (k) -> getContext().spawn(superviseTaskBehavior(task), k));
        getContext().watchWith(ref, new InternalJobTaskFailed(msg, task.name()));
        return ref;
    }

    private Behavior superviseTaskBehavior(Task t) {
        Behavior behavior = Behaviors.supervise(t.behavior()).onFailure(SupervisorStrategy.stop());
        return behavior;
    }

    private static class InternalAdaptedProcessorCommand implements TaskCommand {

        public final ProcessorCommand processorCommand;

        public InternalAdaptedProcessorCommand(ProcessorCommand msg) {
            super();
            this.processorCommand = msg;
        }

    }

    private static class InternalJobTaskFailed implements TaskCommand {

        public final URI uri;
        public final URI itemURI;
        public final String taskName;

        public InternalJobTaskFailed(JobTaskToProcess msg, String taskName) {
            this.uri = msg.uri;
            this.itemURI = msg.itemURI;
            this.taskName = taskName;
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy