X-Git-Url: https://git.pterodactylus.net/?p=rhynodge.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Freactor%2Fengine%2FEngine.java;h=74b01b1226a6219585e3b297f67b46c4392b9e94;hp=9edbdb4e06f3067507e7912d2c7acf91da23a144;hb=a4a82f14d760074339ef88c1245a88f6edf4cde8;hpb=9cd054fb2fec1c2a1f0f24b3b88f477720563094 diff --git a/src/main/java/net/pterodactylus/reactor/engine/Engine.java b/src/main/java/net/pterodactylus/reactor/engine/Engine.java index 9edbdb4..74b01b1 100644 --- a/src/main/java/net/pterodactylus/reactor/engine/Engine.java +++ b/src/main/java/net/pterodactylus/reactor/engine/Engine.java @@ -17,10 +17,10 @@ package net.pterodactylus.reactor.engine; +import java.util.HashMap; import java.util.Map; -import java.util.Set; +import java.util.Map.Entry; import java.util.SortedMap; -import java.util.concurrent.TimeUnit; import net.pterodactylus.reactor.Filter; import net.pterodactylus.reactor.Query; @@ -28,13 +28,13 @@ import net.pterodactylus.reactor.Reaction; import net.pterodactylus.reactor.Trigger; import net.pterodactylus.reactor.states.AbstractState; import net.pterodactylus.reactor.states.FailedState; +import net.pterodactylus.reactor.states.StateManager; +import org.apache.commons.lang3.tuple.Pair; import org.apache.log4j.Logger; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.google.common.util.concurrent.AbstractExecutionThreadService; -import com.google.common.util.concurrent.Uninterruptibles; /** * Reactor main engine. @@ -46,11 +46,12 @@ public class Engine extends AbstractExecutionThreadService { /** The logger. */ private static final Logger logger = Logger.getLogger(Engine.class); - /** All defined reactions. */ - private final Set reactions = Sets.newHashSet(); + /** The state manager. */ + private final StateManager stateManager = new StateManager("states"); - /** Reaction states. */ - private final Map reactionExecutions = Maps.newHashMap(); + /** All defined reactions. */ + /* synchronize on itself. */ + private final Map reactions = new HashMap(); // // ACCESSORS @@ -59,13 +60,38 @@ public class Engine extends AbstractExecutionThreadService { /** * Adds the given reaction to this engine. * + * @param name + * The name of the reaction * @param reaction * The reaction to add to this engine + * @throws IllegalStateException + * if the engine already contains a {@link Reaction} with the + * given name + */ + public void addReaction(String name, Reaction reaction) { + synchronized (reactions) { + if (reactions.containsKey(name)) { + throw new IllegalStateException(String.format("Engine already contains a Reaction named “%s!”", name)); + } + reactions.put(name, reaction); + reactions.notifyAll(); + } + } + + /** + * Removes the reaction with the given name. + * + * @param name + * The name of the reaction to remove */ - @SuppressWarnings("synthetic-access") - public void addReaction(Reaction reaction) { - reactions.add(reaction); - reactionExecutions.put(reaction, new ReactionExecution()); + public void removeReaction(String name) { + synchronized (reactions) { + if (!reactions.containsKey(name)) { + return; + } + reactions.remove(name); + reactions.notifyAll(); + } } // @@ -80,44 +106,53 @@ public class Engine extends AbstractExecutionThreadService { while (isRunning()) { /* delay if we have no reactions. */ - if (reactions.isEmpty()) { - logger.trace("Sleeping for 1 second while no Reactions available."); - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - continue; + synchronized (reactions) { + if (reactions.isEmpty()) { + logger.debug("Sleeping while no Reactions available."); + try { + reactions.wait(); + } catch (InterruptedException ie1) { + /* ignore, we’re looping anyway. */ + } + continue; + } } /* find next reaction. */ - SortedMap nextReactions = Maps.newTreeMap(); - for (Reaction reaction : reactions) { - ReactionExecution reactionExecution = reactionExecutions.get(reaction); - nextReactions.put(reactionExecution.lastExecutionTime() + reaction.updateInterval(), reaction); + SortedMap> nextReactions = Maps.newTreeMap(); + String reactionName; + Reaction nextReaction; + synchronized (reactions) { + for (Entry reactionEntry : reactions.entrySet()) { + net.pterodactylus.reactor.State state = stateManager.loadState(reactionEntry.getKey()); + long stateTime = (state != null) ? state.time() : 0; + nextReactions.put(stateTime + reactionEntry.getValue().updateInterval(), Pair.of(reactionEntry.getKey(), reactionEntry.getValue())); + } + reactionName = nextReactions.get(nextReactions.firstKey()).getLeft(); + nextReaction = nextReactions.get(nextReactions.firstKey()).getRight(); } - Reaction nextReaction = nextReactions.get(nextReactions.firstKey()); - ReactionExecution reactionExecution = reactionExecutions.get(nextReaction); logger.debug(String.format("Next Reaction: %s.", nextReaction)); /* wait until the next reaction has to run. */ - while (isRunning()) { - long waitTime = (reactionExecution.lastExecutionTime() + nextReaction.updateInterval()) - System.currentTimeMillis(); - logger.debug(String.format("Time to wait for next Reaction: %d millseconds.", waitTime)); - if (waitTime <= 0) { - break; + net.pterodactylus.reactor.State lastState = stateManager.loadState(reactionName); + long lastStateTime = (lastState != null) ? lastState.time() : 0; + long waitTime = (lastStateTime + nextReaction.updateInterval()) - System.currentTimeMillis(); + logger.debug(String.format("Time to wait for next Reaction: %d millseconds.", waitTime)); + if (waitTime > 0) { + synchronized (reactions) { + try { + logger.debug(String.format("Waiting for %d milliseconds.", waitTime)); + reactions.wait(waitTime); + } catch (InterruptedException ie1) { + /* we’re looping! */ + } } - try { - logger.debug(String.format("Waiting for %d milliseconds.", waitTime)); - TimeUnit.MILLISECONDS.sleep(waitTime); - } catch (InterruptedException ie1) { - /* we’re looping! */ - } - } - /* are we still running? */ - if (!isRunning()) { - break; + /* re-start loop to check for new reactions. */ + continue; } /* run reaction. */ - reactionExecution.setLastExecutionTime(System.currentTimeMillis()); Query query = nextReaction.query(); net.pterodactylus.reactor.State state; try { @@ -144,15 +179,15 @@ public class Engine extends AbstractExecutionThreadService { } } if (state.success()) { - reactionExecution.addState(state); + stateManager.saveState(reactionName, state); } /* only run trigger if we have collected two states. */ Trigger trigger = nextReaction.trigger(); boolean triggerHit = false; - if ((reactionExecution.previousState() != null) && state.success()) { + if ((lastState != null) && state.success()) { logger.debug("Checking Trigger for changes..."); - triggerHit = trigger.triggers(reactionExecution.currentState(), reactionExecution.previousState()); + triggerHit = trigger.triggers(state, lastState); } /* run action if trigger was hit. */ @@ -165,88 +200,4 @@ public class Engine extends AbstractExecutionThreadService { } } - /** - * Stores execution states of a {@link Reaction}. - * - * @author David ‘Bombe’ Roden - */ - private static class ReactionExecution { - - /** The time the reaction was last executed. */ - private long lastExecutionTime; - - /** The previous state of the reaction. */ - private net.pterodactylus.reactor.State previousState; - - /** The current state of the reaction. */ - private net.pterodactylus.reactor.State currentState; - - // - // ACCESSORS - // - - /** - * Returns the time the reaction was last executed. If the reaction was - * not yet executed, this method returns {@code 0}. - * - * @return The last execution time of the reaction (in milliseconds - * since Jan 1, 1970 UTC) - */ - public long lastExecutionTime() { - return lastExecutionTime; - } - - /** - * Returns the current state of the reaction. If the reaction was not - * yet executed, this method returns {@code null}. - * - * @return The current state of the reaction - */ - public net.pterodactylus.reactor.State currentState() { - return currentState; - } - - /** - * Returns the previous state of the reaction. If the reaction was not - * yet executed at least twice, this method returns {@code null}. - * - * @return The previous state of the reaction - */ - public net.pterodactylus.reactor.State previousState() { - return previousState; - } - - /** - * Sets the last execution time of the reaction. - * - * @param lastExecutionTime - * The last execution time of the reaction (in milliseconds - * since Jan 1, 1970 UTC) - * @return This execution - */ - public ReactionExecution setLastExecutionTime(long lastExecutionTime) { - this.lastExecutionTime = lastExecutionTime; - return this; - } - - // - // ACTIONS - // - - /** - * Adds the given state as current state and moves the current state - * into the previous state. - * - * @param state - * The new current state - * @return This execution - */ - public ReactionExecution addState(net.pterodactylus.reactor.State state) { - previousState = currentState; - currentState = state; - return this; - } - - } - }