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

com.fireflysource.common.actor.AbstractActor Maven / Gradle / Ivy

The newest version!
package com.fireflysource.common.actor;

import com.fireflysource.common.slf4j.LazyLogger;
import com.fireflysource.common.sys.SystemLogger;

import java.util.Objects;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

abstract public class AbstractActor implements Runnable, Actor, ActorInternalApi {

    private static final LazyLogger log = SystemLogger.create(AbstractActor.class);

    private final String address;
    private final Dispatcher dispatcher;
    private final Mailbox mailbox;
    private final AtomicReference taskState = new AtomicReference<>(TaskState.IDLE);
    private ActorState actorState = ActorState.RUNNING;

    public AbstractActor() {
        this(UUID.randomUUID().toString(), DispatcherFactory.createDispatcher(), MailboxFactory.createMailbox());
    }

    public AbstractActor(String address, Dispatcher dispatcher, Mailbox mailbox) {
        this.address = address;
        this.dispatcher = dispatcher;
        this.mailbox = mailbox;
    }

    @Override
    public String getAddress() {
        return address;
    }

    @Override
    public boolean send(T message) {
        if (mailbox.offerUserMessage(message)) {
            dispatch();
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void pause() {
        sendSystemMessage(SystemMessage.PAUSE);
    }

    @Override
    public void resume() {
        sendSystemMessage(SystemMessage.RESUME);
    }

    @Override
    public void shutdown() {
        sendSystemMessage(SystemMessage.SHUTDOWN);
    }

    @Override
    public void restart() {
        sendSystemMessage(SystemMessage.RESTART);
    }

    @Override
    public ActorState getActorState() {
        return actorState;
    }

    @Override
    public void run() {
        while (true) {
            handleSystemMessages();

            if (actorState == ActorState.PAUSE) {
                break;
            }

            boolean empty = handleUserMessages();
            if (empty) {
                break;
            }
        }

        dispatchNext();
    }

    private void dispatchNext() {
        taskState.set(TaskState.IDLE);
        switch (actorState) {
            case SHUTDOWN:
            case RUNNING:
                if (mailbox.hasSystemMessage() || mailbox.hasUserMessage()) {
                    dispatch();
                }
                break;
            case PAUSE:
                if (mailbox.hasSystemMessage()) {
                    dispatch();
                }
                break;
        }
    }

    private boolean handleUserMessages() {
        boolean empty;
        T message = mailbox.pollUserMessage();
        if (message != null) {
            switch (actorState) {
                case RUNNING:
                    handleMessage(message);
                    break;
                case SHUTDOWN:
                    handleDiscardMessage(message);
                    break;
            }
            empty = false;
        } else {
            empty = true;
        }
        return empty;
    }

    private void handleSystemMessages() {
        SystemMessage systemMessage = mailbox.pollSystemMessage();
        if (systemMessage != null) {
            switch (systemMessage) {
                case PAUSE:
                    if (actorState == ActorState.RUNNING) {
                        actorState = ActorState.PAUSE;
                    }
                    break;
                case RESUME:
                    if (actorState == ActorState.PAUSE) {
                        actorState = ActorState.RUNNING;
                    }
                    break;
                case SHUTDOWN:
                    actorState = ActorState.SHUTDOWN;
                    break;
                case RESTART:
                    if (actorState == ActorState.SHUTDOWN) {
                        actorState = ActorState.RUNNING;
                    }
                    break;
            }
        }
    }

    private void sendSystemMessage(SystemMessage message) {
        if (mailbox.offerSystemMessage(message)) {
            dispatch();
        }
    }

    private void dispatch() {
        if (taskState.compareAndSet(TaskState.IDLE, TaskState.BUSY)) {
            dispatcher.dispatch(this);
        }
    }

    private void handleMessage(T message) {
        try {
            onReceive(message);
        } catch (Exception e) {
            log.error("on receive exception. address: " + getAddress(), e);
        }
    }

    private void handleDiscardMessage(T message) {
        try {
            onDiscard(message);
        } catch (Exception e) {
            log.error("on discard exception. address: " + getAddress(), e);
        }
    }

    abstract public void onReceive(T message);

    public void onDiscard(T message) {

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AbstractActor that = (AbstractActor) o;
        return address.equals(that.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(address);
    }

    enum TaskState {
        IDLE, BUSY
    }

    public enum SystemMessage {
        PAUSE, RESUME, SHUTDOWN, RESTART
    }

    public static class DispatcherImpl implements Dispatcher {
        private final Executor executor;

        public DispatcherImpl(Executor executor) {
            this.executor = executor;
        }

        @Override
        public void dispatch(Runnable runnable) {
            executor.execute(runnable);
        }
    }

    public static class MailboxImpl implements Mailbox {
        private final Queue userMessageQueue;
        private final Queue systemMessageQueue;
        private final AtomicInteger unhandledUserMessageCount = new AtomicInteger(0);
        private final AtomicInteger unhandledSystemMessageCount = new AtomicInteger(0);

        public MailboxImpl(Queue userMessageQueue, Queue systemMessageQueue) {
            this.userMessageQueue = userMessageQueue;
            this.systemMessageQueue = systemMessageQueue;
        }

        @Override
        public AbstractActor.SystemMessage pollSystemMessage() {
            AbstractActor.SystemMessage systemMessage = systemMessageQueue.poll();
            if (systemMessage != null) {
                unhandledSystemMessageCount.decrementAndGet();
            }
            return systemMessage;
        }

        @Override
        public boolean offerSystemMessage(AbstractActor.SystemMessage systemMessage) {
            boolean success = systemMessageQueue.offer(systemMessage);
            if (success) {
                unhandledSystemMessageCount.incrementAndGet();
            }
            return success;
        }

        @Override
        public boolean hasSystemMessage() {
            return unhandledSystemMessageCount.get() > 0;
        }

        @Override
        public T pollUserMessage() {
            T message = userMessageQueue.poll();
            if (message != null) {
                unhandledUserMessageCount.decrementAndGet();
            }
            return message;
        }

        @Override
        public boolean offerUserMessage(T userMessage) {
            boolean success = userMessageQueue.offer(userMessage);
            if (success) {
                unhandledUserMessageCount.incrementAndGet();
            }
            return success;
        }

        @Override
        public boolean hasUserMessage() {
            return unhandledUserMessageCount.get() > 0;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy