$darkmode
DENOPTIM
ExternalCmdsListener.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2022 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.ga;
20
21import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
22import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
23
24import java.io.File;
25import java.io.IOException;
26import java.nio.file.FileSystems;
27import java.nio.file.Path;
28import java.nio.file.WatchEvent;
29import java.nio.file.WatchKey;
30import java.nio.file.WatchService;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.HashSet;
34import java.util.Set;
35import java.util.logging.Level;
36import java.util.logging.Logger;
37
38import denoptim.constants.DENOPTIMConstants;
39import denoptim.exception.DENOPTIMException;
40import denoptim.exception.ExceptionUtils;
41import denoptim.io.DenoptimIO;
42import denoptim.logging.StaticLogger;
43import denoptim.programs.denovo.GAParameters;
44
53public class ExternalCmdsListener implements Runnable
54{
55
56 private final Path pathname;
57 private final WatchService watcher;
58 private final WatchKey key;
60
64 private boolean intendedStop = false;
65
66 private final String NL = System.getProperty("line.separator");
67
71 private Logger logger = null;
72
73//------------------------------------------------------------------------------
74
75 public ExternalCmdsListener(Path pathname, Logger logger) throws IOException
76 {
77 this.pathname = pathname;
78 this.logger = logger;
79 logger.log(Level.INFO, "Watching pathname '" + pathname + "' "
80 + "for live instructions.");
81 this.watcher = FileSystems.getDefault().newWatchService();
82 this.key = pathname.register(watcher, ENTRY_CREATE);
83 }
84
85//------------------------------------------------------------------------------
86
90 @SuppressWarnings("unchecked")
91 public void run()
92 {
93 try {
94 for (;;)
95 {
96 // this will wait for an event (pauses the thread)
97 WatchKey key = watcher.take();
98 if (this.key != key)
99 {
100 // The event is something else than what expected
101 continue;
102 }
103 for (WatchEvent<?> event : key.pollEvents())
104 {
105 WatchEvent.Kind<?> kind = event.kind();
106
107 // There seems to be a possibility for nasty events that need
108 // to be ignored.
109 if (kind == OVERFLOW) {
110 continue;
111 }
112
113 WatchEvent<Path> ev = null;
114 try {
115 ev = (WatchEvent<Path>) event;
116 } catch (Throwable t) {
117 // The event is something else than what expected
118 continue;
119 }
120 Path name = ev.context();
121 Path child = pathname.resolve(name);
122 processExternalCmdFile(child.toFile());
123 }
124
125 // Remove key from the queue of signalled events
126 if (!key.reset())
127 {
128 break;
129 }
130 }
131 } catch (InterruptedException e) {
132 return;
133 } catch (Throwable t) {
134 if (!intendedStop)
135 {
136 String msg = this.getClass().getSimpleName() + " stopped! From "
137 + "now on, we cannot listen to commands from the "
138 + "interface." + DENOPTIMConstants.EOL + " Cause: "
140 if (logger!=null)
141 {
142 logger.log(Level.SEVERE, msg);
143 t.printStackTrace();
144 } else {
145 StaticLogger.appLogger.log(Level.SEVERE, msg);
146 }
147 }
148 }
149 }
150
151//------------------------------------------------------------------------------
152
153 private void processExternalCmdFile(File file)
154 {
155 ArrayList<String> lines = null;
156 try {
157 lines = DenoptimIO.readList(file.getAbsolutePath(), true);
158 } catch (DENOPTIMException e) {
159 logger.log(Level.WARNING, "Unable to read file '"
160 + file.getAbsolutePath() + "'. Any instruction contained "
161 + "in that file is ignored. Hint: " + e.getMessage() + NL);
162 e.printStackTrace();
163 return;
164 }
165
166 if (lines.size() == 0)
167 {
168 // Empty file is probably a sign that the file is being written
169 logger.log(Level.WARNING, "Empty instructions in '"
170 + file.getAbsolutePath() + "'.");
171 }
172
173 for (String line : lines)
174 {
175 if (line.startsWith("STOP_GA"))
176 {
177 logger.log(Level.SEVERE, "GA run will be "
178 + "stopped upon external request from '"
179 + file.getAbsolutePath() + "'." + NL);
180 if (ea != null)
181 {
182 ea.stopRun();
183 }
184 }
185
186 if (line.startsWith("REMOVE_CANDIDATE"))
187 {
188 String candIDs = line.substring(
189 "REMOVE_CANDIDATE".length()).trim();
190 logger.log(Level.SEVERE, "Removing '"
191 + candIDs + "' upon external request from '"
192 + file.getAbsolutePath() + "'." + NL);
193 String[] parts = candIDs.split("\\s+");
194 Set<String> candNames = new HashSet<String>(
195 Arrays.asList(parts));
196 if (ea != null)
197 {
198 ea.removeCandidates(candNames);
199 }
200 }
201
202 if (line.startsWith("ADD_CANDIDATE"))
203 {
204 String fileNamesLst = line.substring(
205 "ADD_CANDIDATE".length()).trim();
206 logger.log(Level.SEVERE, "Adding candidates from '"
207 + fileNamesLst + "' upon external request from '"
208 + file.getAbsolutePath() + "'." + NL);
209 String[] parts = fileNamesLst.split("\\s+");
210 Set<String> paths = new HashSet<String>(Arrays.asList(parts));
211 if (ea != null)
212 {
213 ea.addCandidates(paths);
214 }
215 }
216 }
217 }
218
219//------------------------------------------------------------------------------
220
221 public void closeWatcher() throws IOException
222 {
223 intendedStop = true;
224 this.watcher.close();
225 }
226
227//------------------------------------------------------------------------------
228
230 {
231 this.ea = ea;
232 }
233
234//------------------------------------------------------------------------------
235
236}
static String getStackTraceAsString(Throwable t)
Prints the stack trace of an exception into a string.
DENOPTIM's evolutionary algorithm.
void addCandidates(Set< String > pathNames)
Adds candidate IDs to the list of "to-be-included" candidates.
void removeCandidates(Set< String > candID)
Adds candidate IDs to the list of "to-be-removed" candidates.
Service that watches the interface folder (i.e., see GAParameters#interfaceDir) for instructions comi...
Logger logger
Program-specific logger.
void setReferenceToRunningEAlgorithm(EvolutionaryAlgorithm ea)
ExternalCmdsListener(Path pathname, Logger logger)
boolean intendedStop
Flag signaling that we are intentionally interrupting the service.
void run()
Starts listening for events and keeps listening in its own thread.
Utility methods for input/output.
static ArrayList< String > readList(String fileName)
Read list of data as text.
Logger class for DENOPTIM.
static final Logger appLogger