--- /dev/null
+package net.pterodactylus.rhynodge.engine;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Stores general configuration of Rhynodge.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Configuration {
+
+ @JsonProperty
+ private final String smtpHostname = null;
+
+ @JsonProperty
+ private String errorEmailSender = null;
+
+ @JsonProperty
+ private String errorEmailRecipient = null;
+
+ public String getSmtpHostname() {
+ return smtpHostname;
+ }
+
+ public String getErrorEmailSender() {
+ return errorEmailSender;
+ }
+
+ public String getErrorEmailRecipient() {
+ return errorEmailRecipient;
+ }
+
+ public static Configuration from(InputStream inputStream) throws IOException {
+ return new ObjectMapper().readValue(inputStream, Configuration.class);
+ }
+
+}
import java.util.concurrent.ScheduledThreadPoolExecutor;
import net.pterodactylus.rhynodge.Reaction;
+import net.pterodactylus.rhynodge.actions.EmailAction;
import net.pterodactylus.rhynodge.states.StateManager;
/**
private final StateManager stateManager;
private final ScheduledExecutorService executorService;
private final Map<String, Future<?>> scheduledFutures = new ConcurrentHashMap<>();
+ private final EmailAction errorEmailAction;
- /**
- * Creates a new engine.
- *
- * @param stateManager
- * The state manager
- */
- public Engine(StateManager stateManager) {
+ public Engine(StateManager stateManager, EmailAction errorEmailAction) {
this.stateManager = stateManager;
+ this.errorEmailAction = errorEmailAction;
executorService = new ScheduledThreadPoolExecutor(10);
}
Optional<net.pterodactylus.rhynodge.State> lastState = reactionState.loadLastState();
long lastExecutionTime = lastState.map(net.pterodactylus.rhynodge.State::time).orElse(0L);
long nextExecutionTime = lastExecutionTime + reaction.updateInterval();
- ReactionRunner reactionRunner = new ReactionRunner(reaction, reactionState);
+ ReactionRunner reactionRunner = new ReactionRunner(reaction, reactionState, errorEmailAction);
ScheduledFuture<?> future = executorService.scheduleWithFixedDelay(reactionRunner, nextExecutionTime - currentTimeMillis(), reaction.updateInterval(), MILLISECONDS);
scheduledFutures.put(name, future);
}
import static net.pterodactylus.rhynodge.states.FailedState.INSTANCE;
import static org.apache.log4j.Logger.getLogger;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Optional;
import net.pterodactylus.rhynodge.Action;
import net.pterodactylus.rhynodge.Reaction;
import net.pterodactylus.rhynodge.State;
import net.pterodactylus.rhynodge.Trigger;
+import net.pterodactylus.rhynodge.actions.EmailAction;
+import net.pterodactylus.rhynodge.output.DefaultOutput;
+import net.pterodactylus.rhynodge.output.Output;
import net.pterodactylus.rhynodge.states.FailedState;
import org.apache.log4j.Logger;
private static final Logger logger = getLogger(ReactionRunner.class);
private final Reaction reaction;
private final ReactionState reactionState;
+ private final EmailAction errorEmailAction;
- public ReactionRunner(Reaction reaction, ReactionState reactionState) {
+ public ReactionRunner(Reaction reaction, ReactionState reactionState, EmailAction errorEmailAction) {
this.reactionState = reactionState;
this.reaction = reaction;
+ this.errorEmailAction = errorEmailAction;
}
@Override
if (!state.success()) {
logger.info(format("Reaction %s failed.", reaction.name()));
saveStateWithIncreasedFailCount(state);
+ errorEmailAction.execute(createErrorOutput(reaction, state));
return;
}
Optional<State> lastSuccessfulState = reactionState.loadLastSuccessfulState();
reactionState.saveState(state);
}
+ private Output createErrorOutput(Reaction reaction, State state) {
+ DefaultOutput output = new DefaultOutput(String.format("Error while processing “%s!”", reaction.name()));
+ output.addText("text/plain; charset=utf-8", createEmailText(reaction, state));
+ return output;
+ }
+
+ private String createEmailText(Reaction reaction, State state) {
+ StringBuilder emailText = new StringBuilder();
+ emailText.append(String.format("An error occured while processing “.”\n\n", reaction.name()));
+ appendExceptionToEmailText(state.exception(), emailText);
+ return emailText.toString();
+ }
+
+ private void appendExceptionToEmailText(Throwable exception, StringBuilder emailText) {
+ if (exception != null) {
+ try (StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter)) {
+ exception.printStackTrace(printWriter);
+ emailText.append(stringWriter.toString());
+ } catch (IOException ioe1) {
+ /* StringWriter doesn’t throw. */
+ throw new RuntimeException(ioe1);
+ }
+ }
+ }
+
private State runQuery() {
logger.info(format("Querying %s...", reaction.name()));
try {
package net.pterodactylus.rhynodge.engine;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import net.pterodactylus.rhynodge.actions.EmailAction;
import net.pterodactylus.rhynodge.loader.ChainWatcher;
import net.pterodactylus.rhynodge.states.StateManager;
* @param arguments
* Command-line arguments
*/
- public static void main(String... arguments) {
+ public static void main(String... arguments) throws IOException {
/* parse command line. */
Parameters parameters = CliFactory.parseArguments(Parameters.class, arguments);
+ Configuration configuration = loadConfiguration(parameters.getConfigurationFile());
/* create the state manager. */
StateManager stateManager = new StateManager(parameters.getStateDirectory());
/* create the engine. */
- Engine engine = new Engine(stateManager);
+ Engine engine = new Engine(stateManager, createErrorEmailAction(configuration));
/* start a watcher. */
ChainWatcher chainWatcher = new ChainWatcher(engine, parameters.getChainDirectory());
chainWatcher.start();
}
+ private static Configuration loadConfiguration(String configurationFile) throws IOException {
+ try (FileInputStream configInputStream = new FileInputStream(configurationFile)) {
+ return Configuration.from(configInputStream);
+ }
+ }
+
+ private static EmailAction createErrorEmailAction(Configuration configuration) {
+ return new EmailAction(configuration.getSmtpHostname(), configuration.getErrorEmailSender(), configuration.getErrorEmailRecipient());
+ }
+
/**
* Definition of the command-line parameters.
*
@Option(defaultValue = "states", longName = "states", shortName = "s", description = "The directory to store states in")
String getStateDirectory();
+ @Option(defaultValue = "/etc/rhynodge/rhynodge.json", longName = "config", shortName = "C", description = "The name of the configuration file")
+ String getConfigurationFile();
+
}
}
--- /dev/null
+package net.pterodactylus.rhynodge.engine;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link Configuration}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ConfigurationTest {
+
+ @Test
+ public void configurationCanBeReadFromJsonFile() throws IOException {
+ InputStream inputStream = getClass().getResourceAsStream("configuration.json");
+ Configuration configuration = Configuration.from(inputStream);
+ MatcherAssert.assertThat(configuration.getSmtpHostname(), Matchers.is("localhost"));
+ MatcherAssert.assertThat(configuration.getErrorEmailSender(), Matchers.is("errors@rhynodge.net"));
+ MatcherAssert.assertThat(configuration.getErrorEmailRecipient(), Matchers.is("errors@user.net"));
+ }
+
+}
--- /dev/null
+{
+ "smtpHostname": "localhost",
+ "errorEmailSender": "errors@rhynodge.net",
+ "errorEmailRecipient": "errors@user.net"
+}