2 * Rhynodge - StateManager.java - Copyright © 2013 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.rhynodge.states;
20 import static java.util.Optional.empty;
21 import static java.util.Optional.ofNullable;
24 import java.io.IOException;
25 import java.util.Optional;
27 import com.fasterxml.jackson.databind.SerializationFeature;
28 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
29 import com.fasterxml.jackson.module.kotlin.KotlinModule;
30 import jakarta.inject.Inject;
31 import jakarta.inject.Singleton;
33 import net.pterodactylus.rhynodge.State;
35 import org.apache.log4j.Logger;
37 import com.fasterxml.jackson.core.JsonGenerationException;
38 import com.fasterxml.jackson.core.JsonParseException;
39 import com.fasterxml.jackson.databind.JsonMappingException;
40 import com.fasterxml.jackson.databind.ObjectMapper;
43 * Loads and saves {@link State}s.
45 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
48 public class StateManager {
51 private static final Logger logger = Logger.getLogger(StateManager.class);
53 /** Jackson object mapper. */
54 private final ObjectMapper objectMapper = new ObjectMapper();
57 objectMapper.registerModule(new KotlinModule.Builder().build());
58 objectMapper.registerModule(new JavaTimeModule());
59 objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
62 /** The directory in which to store states. */
63 private final String directory;
66 * Creates a new state manager. The given directory is assumed to exist.
68 * @param stateDirectory
69 * The directory to store states in
72 public StateManager(StateDirectory stateDirectory) {
73 this.directory = stateDirectory.getDirectory();
81 * Loads the last state with the given name.
84 * The name of the reaction
85 * @return The loaded state, or {@link Optional#empty()} if the state could not be
88 public Optional<State> loadLastState(String reactionName) {
89 return loadLastState(reactionName, false);
93 * Loads the last state with the given name.
96 * The name of the reaction
97 * @return The loaded state, or {@link Optional#empty()} if the state could not be
100 public Optional<State> loadLastSuccessfulState(String reactionName) {
101 return loadLastState(reactionName, true);
105 * Saves the given state under the given name.
107 * @param reactionName
108 * The name of the reaction
112 public void saveState(String reactionName, State state) {
113 File stateFile = null;
115 stateFile = stateFile(reactionName, "last");
116 objectMapper.writeValue(stateFile, state);
117 if (state.success()) {
118 stateFile = stateFile(reactionName, "success");
119 objectMapper.writeValue(stateFile, state);
121 } catch (JsonGenerationException jge1) {
122 logger.warn(String.format("State for Reaction “%s” could not be generated.", reactionName), jge1);
124 } catch (JsonMappingException jme1) {
125 logger.warn(String.format("State for Reaction “%s” could not be generated.", reactionName), jme1);
127 } catch (IOException ioe1) {
128 logger.warn(String.format("State for Reaction “%s” could not be written.", reactionName));
138 * Returns the file for the state with the given name.
140 * @param reactionName
141 * The name of the reaction
143 * An additional suffix (may be {@code null}
144 * @return The file for the state
146 private File stateFile(String reactionName, String suffix) {
147 return new File(directory, reactionName + ((suffix != null) ? "." + suffix : "") + ".json");
151 * Load the given state for the reaction with the given name.
153 * @param reactionName
154 * The name of the reaction
156 * {@code true} to load the last successful state, {@code false}
157 * to load the last state
158 * @return The loaded state, or {@link Optional#empty()} if the state could not be
161 private Optional<State> loadLastState(String reactionName, boolean successful) {
162 File stateFile = stateFile(reactionName, successful ? "success" : "last");
164 State state = objectMapper.readValue(stateFile, AbstractState.class);
165 return ofNullable(state);
166 } catch (JsonParseException jpe1) {
167 logger.warn(String.format("State for Reaction “%s” could not be parsed.", reactionName), jpe1);
168 } catch (JsonMappingException jme1) {
169 logger.warn(String.format("State for Reaction “%s” could not be parsed.", reactionName), jme1);
170 } catch (IOException ioe1) {
171 logger.info(String.format("State for Reaction “%s” could not be found.", reactionName));
176 public static class StateDirectory {
178 private final String directory;
180 private StateDirectory(String directory) {
181 this.directory = directory;
184 public String getDirectory() {
188 public static StateDirectory of(String directory) {
189 return new StateDirectory(directory);