a6a5004be6440b6b9dc0d9183c8bfb7c4a4278d0
[rhynodge.git] / src / main / java / net / pterodactylus / reactor / loader / ChainWatcher.java
1 /*
2  * Reactor - ChainWatcher.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.reactor.loader;
19
20 import java.io.File;
21 import java.io.FilenameFilter;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.concurrent.TimeUnit;
26
27 import javax.xml.bind.JAXBContext;
28 import javax.xml.bind.JAXBException;
29 import javax.xml.bind.Unmarshaller;
30
31 import net.pterodactylus.reactor.Reaction;
32 import net.pterodactylus.reactor.engine.Engine;
33 import net.pterodactylus.reactor.loader.Chain.Parameter;
34 import net.pterodactylus.reactor.loader.Chain.Part;
35
36 import org.apache.log4j.Logger;
37
38 import com.google.common.base.Predicate;
39 import com.google.common.collect.Maps;
40 import com.google.common.util.concurrent.AbstractExecutionThreadService;
41 import com.google.common.util.concurrent.Uninterruptibles;
42
43 /**
44  * Watches a directory for chain XML files and loads and unloads
45  * {@link Reaction}s from the {@link Engine}.
46  *
47  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
48  */
49 public class ChainWatcher extends AbstractExecutionThreadService {
50
51         /** The logger. */
52         private static final Logger logger = Logger.getLogger(ChainWatcher.class);
53
54         /** The reaction loader. */
55         private final ReactionLoader reactionLoader = new ReactionLoader();
56
57         /** The engine to load reactions with. */
58         private final Engine engine;
59
60         /** The directory to watch for chain XML files. */
61         private final String directory;
62
63         /**
64          * Creates a new chain watcher.
65          *
66          * @param engine
67          *            The engine to load reactions with
68          * @param directory
69          *            The directory to watch
70          */
71         public ChainWatcher(Engine engine, String directory) {
72                 this.engine = engine;
73                 this.directory = directory;
74         }
75
76         //
77         // ABSTRACTEXECUTIONTHREADSERVICE METHODS
78         //
79
80         /**
81          * {@inheritDoc}
82          */
83         @Override
84         protected void run() throws Exception {
85
86                 /* loaded chains. */
87                 final Map<String, Chain> loadedChains = new HashMap<String, Chain>();
88
89                 while (isRunning()) {
90
91                         /* check if directory is there. */
92                         File directoryFile = new File(directory);
93                         if (!directoryFile.exists() || !directoryFile.isDirectory() || !directoryFile.canRead()) {
94                                 Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
95                                 continue;
96                         }
97
98                         /* list all files, scan for XMLs. */
99                         logger.debug(String.format("Scanning %s...", directory));
100                         File[] xmlFiles = directoryFile.listFiles(new FilenameFilter() {
101
102                                 @Override
103                                 public boolean accept(File dir, String name) {
104                                         return name.endsWith(".xml");
105                                 }
106                         });
107                         logger.debug(String.format("Found %d XML file(s), parsing...", xmlFiles.length));
108
109                         /* now parse all XML files. */
110                         Map<String, Chain> chains = new HashMap<String, Chain>();
111                         for (File xmlFile : xmlFiles) {
112
113                                 /* parse XML file. */
114                                 Chain chain = parseXmlFile(xmlFile);
115
116                                 /* dump chain */
117                                 logger.debug(String.format(" Enabled: %s", chain.enabled()));
118
119                                 logger.debug(String.format(" Query: %s", chain.query().name()));
120                                 for (Parameter parameter : chain.query().parameters()) {
121                                         logger.debug(String.format("  Parameter: %s=%s", parameter.name(), parameter.value()));
122                                 }
123                                 for (Part filter : chain.filters()) {
124                                         logger.debug(String.format(" Filter: %s", filter.name()));
125                                         for (Parameter parameter : filter.parameters()) {
126                                                 logger.debug(String.format("  Parameter: %s=%s", parameter.name(), parameter.value()));
127                                         }
128                                 }
129                                 logger.debug(String.format(" Trigger: %s", chain.trigger().name()));
130                                 for (Parameter parameter : chain.trigger().parameters()) {
131                                         logger.debug(String.format("  Parameter: %s=%s", parameter.name(), parameter.value()));
132                                 }
133                                 logger.debug(String.format(" Action: %s", chain.action().name()));
134                                 for (Parameter parameter : chain.action().parameters()) {
135                                         logger.debug(String.format("  Parameter: %s=%s", parameter.name(), parameter.value()));
136                                 }
137
138                                 chains.put(xmlFile.getName(), chain);
139                         }
140
141                         /* filter enabled chains. */
142                         Map<String, Chain> enabledChains = Maps.filterEntries(chains, new Predicate<Entry<String, Chain>>() {
143
144                                 @Override
145                                 public boolean apply(Entry<String, Chain> chainEntry) {
146                                         return chainEntry.getValue().enabled();
147                                 }
148                         });
149                         logger.debug(String.format("Found %d enabled Chain(s).", enabledChains.size()));
150
151                         /* check for removed chains. */
152                         for (Entry<String, Chain> loadedChain : loadedChains.entrySet()) {
153
154                                 /* skip chains that still exist. */
155                                 if (enabledChains.containsKey(loadedChain.getKey())) {
156                                         continue;
157                                 }
158
159                                 logger.info(String.format("Removing Chain: %s", loadedChain.getKey()));
160                                 engine.removeReaction(loadedChain.getKey());
161                                 loadedChains.remove(loadedChain.getKey());
162                         }
163
164                         /* check for new chains. */
165                         for (Entry<String, Chain> enabledChain : enabledChains.entrySet()) {
166
167                                 /* skip already loaded chains. */
168                                 if (loadedChains.containsValue(enabledChain.getValue())) {
169                                         continue;
170                                 }
171
172                                 logger.info(String.format("Loading new Chain: %s", enabledChain.getKey()));
173
174                                 Reaction reaction = reactionLoader.loadReaction(enabledChain.getValue());
175                                 engine.addReaction(enabledChain.getKey(), reaction);
176                                 loadedChains.put(enabledChain.getKey(), enabledChain.getValue());
177                         }
178
179                         /* wait before checking again. */
180                         Uninterruptibles.sleepUninterruptibly(5, TimeUnit.SECONDS);
181                 }
182         }
183
184         //
185         // STATIC METHODS
186         //
187
188         /**
189          * Parses the given XML file into a {@link Chain}.
190          *
191          * @param xmlFile
192          *            The XML file to parse
193          * @return The parsed chain
194          */
195         private static Chain parseXmlFile(File xmlFile) {
196                 try {
197                         JAXBContext context = JAXBContext.newInstance(Chain.class);
198                         Unmarshaller unmarshaller = context.createUnmarshaller();
199                         logger.debug(String.format("Reading %s...", xmlFile.getPath()));
200                         return (Chain) unmarshaller.unmarshal(xmlFile);
201                 } catch (JAXBException e) {
202                         e.printStackTrace();
203                         return null;
204                 }
205         }
206
207 }