8e0c72eb1177efb568744ddd2aa587116aa99a61
[rhynodge.git] / src / main / java / net / pterodactylus / rhynodge / states / StateManager.java
1 /*
2  * Rhynodge - StateManager.java - Copyright © 2013 David Roden
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.pterodactylus.rhynodge.states;
19
20 import static java.util.Optional.empty;
21 import static java.util.Optional.ofNullable;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.util.Optional;
26
27 import javax.inject.Inject;
28 import javax.inject.Singleton;
29
30 import net.pterodactylus.rhynodge.State;
31
32 import org.apache.log4j.Logger;
33
34 import com.fasterxml.jackson.core.JsonGenerationException;
35 import com.fasterxml.jackson.core.JsonParseException;
36 import com.fasterxml.jackson.databind.JsonMappingException;
37 import com.fasterxml.jackson.databind.ObjectMapper;
38
39 /**
40  * Loads and saves {@link State}s.
41  *
42  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
43  */
44 @Singleton
45 public class StateManager {
46
47         /** The logger. */
48         private static final Logger logger = Logger.getLogger(StateManager.class);
49
50         /** Jackson object mapper. */
51         private final ObjectMapper objectMapper = new ObjectMapper();
52
53         /** The directory in which to store states. */
54         private final String directory;
55
56         /**
57          * Creates a new state manager. The given directory is assumed to exist.
58          *
59          * @param stateDirectory
60          *            The directory to store states in
61          */
62         @Inject
63         public StateManager(StateDirectory stateDirectory) {
64                 this.directory = stateDirectory.getDirectory();
65         }
66
67         //
68         // ACTIONS
69         //
70
71         /**
72          * Loads the last state with the given name.
73          *
74          * @param reactionName
75          *            The name of the reaction
76          * @return The loaded state, or {@link Optional#empty()} if the state could not be
77          *         loaded
78          */
79         public Optional<State> loadLastState(String reactionName) {
80                 return loadLastState(reactionName, false);
81         }
82
83         /**
84          * Loads the last state with the given name.
85          *
86          * @param reactionName
87          *            The name of the reaction
88          * @return The loaded state, or {@link Optional#empty()} if the state could not be
89          *         loaded
90          */
91         public Optional<State> loadLastSuccessfulState(String reactionName) {
92                 return loadLastState(reactionName, true);
93         }
94
95         /**
96          * Saves the given state under the given name.
97          *
98          * @param reactionName
99          *            The name of the reaction
100          * @param state
101          *            The state to save
102          */
103         public void saveState(String reactionName, State state) {
104                 File stateFile = null;
105                 try {
106                         stateFile = stateFile(reactionName, "last");
107                         objectMapper.writeValue(stateFile, state);
108                         if (state.success()) {
109                                 stateFile = stateFile(reactionName, "success");
110                                 objectMapper.writeValue(stateFile, state);
111                         }
112                 } catch (JsonGenerationException jge1) {
113                         logger.warn(String.format("State for Reaction “%s” could not be generated.", reactionName), jge1);
114                         stateFile.delete();
115                 } catch (JsonMappingException jme1) {
116                         logger.warn(String.format("State for Reaction “%s” could not be generated.", reactionName), jme1);
117                         stateFile.delete();
118                 } catch (IOException ioe1) {
119                         logger.warn(String.format("State for Reaction “%s” could not be written.", reactionName));
120                         stateFile.delete();
121                 }
122         }
123
124         //
125         // PRIVATE METHODS
126         //
127
128         /**
129          * Returns the file for the state with the given name.
130          *
131          * @param reactionName
132          *            The name of the reaction
133          * @param suffix
134          *            An additional suffix (may be {@code null}
135          * @return The file for the state
136          */
137         private File stateFile(String reactionName, String suffix) {
138                 return new File(directory, reactionName + ((suffix != null) ? "." + suffix : "") + ".json");
139         }
140
141         /**
142          * Load the given state for the reaction with the given name.
143          *
144          * @param reactionName
145          *            The name of the reaction
146          * @param successful
147          *            {@code true} to load the last successful state, {@code false}
148          *            to load the last state
149          * @return The loaded state, or {@link Optional#empty()} if the state could not be
150          *         loaded
151          */
152         private Optional<State> loadLastState(String reactionName, boolean successful) {
153                 File stateFile = stateFile(reactionName, successful ? "success" : "last");
154                 try {
155                         State state = objectMapper.readValue(stateFile, AbstractState.class);
156                         return ofNullable(state);
157                 } catch (JsonParseException jpe1) {
158                         logger.warn(String.format("State for Reaction “%s” could not be parsed.", reactionName), jpe1);
159                 } catch (JsonMappingException jme1) {
160                         logger.warn(String.format("State for Reaction “%s” could not be parsed.", reactionName), jme1);
161                 } catch (IOException ioe1) {
162                         logger.info(String.format("State for Reaction “%s” could not be found.", reactionName));
163                 }
164                 return empty();
165         }
166
167         public static class StateDirectory {
168
169                 private final String directory;
170
171                 private StateDirectory(String directory) {
172                         this.directory = directory;
173                 }
174
175                 public String getDirectory() {
176                         return directory;
177                 }
178
179                 public static StateDirectory of(String directory) {
180                         return new StateDirectory(directory);
181                 }
182
183         }
184
185 }