$darkmode
DENOPTIM
Main.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.main;
20
21import java.awt.EventQueue;
22import java.io.File;
23import java.io.PrintWriter;
24import java.io.StringWriter;
25import java.lang.reflect.Constructor;
26import java.util.List;
27import java.util.concurrent.ExecutionException;
28
29import org.apache.commons.cli.CommandLine;
30import org.apache.commons.cli.CommandLineParser;
31import org.apache.commons.cli.DefaultParser;
32import org.apache.commons.cli.HelpFormatter;
33import org.apache.commons.cli.ParseException;
34
35import denoptim.constants.DENOPTIMConstants;
36import denoptim.exception.ExceptionUtils;
37import denoptim.files.FileFormat;
38import denoptim.files.FileUtils;
39import denoptim.gui.GUI;
40import denoptim.integration.python.Py4JGetawayServer;
41import denoptim.logging.Version;
42import denoptim.programs.combinatorial.FragSpaceExplorer;
43import denoptim.programs.denovo.GARunner;
44import denoptim.programs.fitnessevaluator.FitnessRunner;
45import denoptim.programs.fragmenter.Fragmenter;
46import denoptim.programs.genetweeker.GeneOpsRunner;
47import denoptim.programs.grapheditor.GraphEditor;
48import denoptim.programs.graphlisthandler.GraphListsHandler;
49import denoptim.programs.isomorphism.Isomorphism;
50import denoptim.programs.moldecularmodelbuilder.MolecularModelBuilder;
51import denoptim.task.ProgramTask;
52import denoptim.task.StaticTaskManager;
53
57public class Main
58{
62 public static enum RunType {
67
72
78
83
89
94
99
104
109
114
119
124
125 // NB: to define a new run type:
126 // 1) add the enum alternative. The order is somewhat related to the
127 // importance (i.e., common use) of a run type.
128 // 2) set the value of "description" in the static block below
129 // 3) set the value of "isCLIEnabled" in the static block below
130 // 4) set the value of "programTaskImpl" in the static block below
131 // 5) consider whether a new parameter file format should be added
132 // in FileFormats.
133
137 private Class<?> programTaskImpl;
138
142 private String description = "";
143
147 private boolean isCLIEnabled = true;
148
153 private boolean needsInputFile = false;
154
155 static {
156 DRY.description = "Dry run";
157 FSE.description = "Combinatorial Fragment Space Exploration";
158 FIT.description = "Stand-alone FITness evaluation";
159 GA.description = "Genetic Algorithm";
160 GE.description = "Stand-alone Graph Editing task";
161 GI.description = "Stand-alone Graph Isomorphism analysis";
162 GO.description = "Stand-alone Genetic Operation";
163 GUI.description = "Graphycal User Interface";
164 CLG.description = "Comparison of Lists of Graphs";
165 B3D.description = "Stand-alone build a 3D molecular model from a "
166 + "graph";
167 FRG.description = "Fragmentation and managment of fragments";
168 PY4J.description = "Starts a server listening to Python";
169
170 DRY.isCLIEnabled = false;
171 FSE.isCLIEnabled = true;
172 FIT.isCLIEnabled = true;
173 GA.isCLIEnabled = true;
174 GE.isCLIEnabled = true;
175 GI.isCLIEnabled = true;
176 GO.isCLIEnabled = true;
177 GUI.isCLIEnabled = false;
178 CLG.isCLIEnabled = true;
179 B3D.isCLIEnabled = true;
180 FRG.isCLIEnabled = true;
181 PY4J.isCLIEnabled = true;
182
183 DRY.needsInputFile = false;
184 FSE.needsInputFile = true;
185 FIT.needsInputFile = true;
186 GA.needsInputFile = true;
187 GE.needsInputFile = true;
188 GI.needsInputFile = true;
189 GO.needsInputFile = true;
190 GUI.needsInputFile = false;
191 CLG.needsInputFile = true;
192 B3D.needsInputFile = true;
193 FRG.needsInputFile = true;
194 PY4J.needsInputFile = false;
195
196 DRY.programTaskImpl = null;
197 FSE.programTaskImpl = FragSpaceExplorer.class;
198 FIT.programTaskImpl = FitnessRunner.class;
199 GA.programTaskImpl = GARunner.class;
200 GE.programTaskImpl = GraphEditor.class;
201 GI.programTaskImpl = Isomorphism.class;
202 GO.programTaskImpl = GeneOpsRunner.class;
203 GUI.programTaskImpl = GUI.class;
204 CLG.programTaskImpl = GraphListsHandler.class;
205 B3D.programTaskImpl = MolecularModelBuilder.class;
206 FRG.programTaskImpl = Fragmenter.class;
207 PY4J.programTaskImpl = null;
208 }
209
214 public Class<?> getProgramTaskImpl()
215 {
216 return programTaskImpl;
217 }
218
224 public static String getRunTypesForUser()
225 {
226 String s = "";
227 String separator = DENOPTIMConstants.EOL;
228 String last = ""; //in case the last entry should use " and "
229 for (int i=0; i<RunType.values().length; i++)
230 {
231 RunType rt = RunType.values()[i];
232 if (!rt.isCLIEnabled)
233 continue; //This run type should not be visible by the user
234 if (i>0 && i < RunType.values().length-2)
235 s = s + " '" + rt.toString() + "' for "+rt.description+ ",";
236 else if (i==0)
237 s = "'" + rt.toString() + "' for "+rt.description+",";
238 else
239 s = s + last+"'"+rt.toString()+"' for "+rt.description+".";
240 s = s + separator;
241 }
242 return s;
243 }
244
251 boolean isCLIEnabled()
252 {
253 return isCLIEnabled;
254 }
255
263 {
264 return needsInputFile;
265 }
266 };
267
268//------------------------------------------------------------------------------
269
276 public static void main(String[] args)
277 {
278 // First, we try to understand what the user wants to do.
279 Behavior behavior = defineProgramBehavior(args);
280
281 // In case of inconsistent requests, we report the error and stop
282 if (behavior.exitStatus!=0)
283 reportError(behavior);
284
285 // Deal with simple requests for information
286 if (RunType.DRY.equals(behavior.runType))
287 {
288 System.out.println(behavior.helpMsg);
289 System.exit(0);
290 }
291
292 // Deal with deamon/server launches
293 if (RunType.PY4J.equals(behavior.runType))
294 {
295 try
296 {
298 } catch (Throwable e)
299 {
300 e.printStackTrace();
301 reportError("Could not start J2PyServer. Hint on problem: "
302 + e.getMessage(), 1);
303 }
304 return;
305 }
306
307 List<String> inputFiles = behavior.cmd.getArgList();
308
309 // We instantiate also the task manager, even if it might not be used.
310 // This is to pre-start the tasks and get a more reliable queue status
311 // at any given time after this point.
313
314 // Now, we deal with proper program runs
315 if (ProgramTask.class.isAssignableFrom(
316 behavior.runType.getProgramTaskImpl()))
317 {
318 File inpFile = null;
319 File wDir = null;
320 if (inputFiles.size()>1)
321 {
322 reportError("Only one input file allowed when requesting run "
323 + behavior.runType + ". Found " + inputFiles.size()
324 + " files: " + inputFiles, 1);
325 }
326 if (behavior.runType.needsInputFile())
327 {
328 if (inputFiles.size()<1)
329 {
330 reportError("Need an input file when requesting run "
331 + behavior.runType + ". Found " + inputFiles.size(), 1);
332 } else if (inputFiles.size()==1) {
333 inpFile = new File(inputFiles.get(0));
334 wDir = inpFile.getParentFile();
335 if (wDir==null)
336 {
337 wDir = new File(System.getProperty("user.dir"));
338 }
339 }
340 }
341 runProgramTask(behavior.runType.getProgramTaskImpl(), inpFile, wDir);
342 terminate();
343 } else if (RunType.GUI.equals(behavior.runType)) {
344 EventQueue.invokeLater(new GUI(behavior.cmd));
345 } else {
346 reportError("You requested a run of type '" + behavior.runType
347 + "', but I found no such implementation. "
348 + "Please, report this to the authors.", 1);
349 }
350 }
351
352//------------------------------------------------------------------------------
353
362 private static void runProgramTask(Class<?> taskClass, File inputFile,
363 File workDir)
364 {
365 if (!ProgramTask.class.isAssignableFrom(taskClass))
366 {
367 reportError("Attempt to create a program task for class '"
368 + taskClass.getSimpleName() + "', but such class is not a "
369 + "extension of '" + ProgramTask.class.getSimpleName()
370 + "'.", 1);
371 }
372
373 ProgramTask task = null;
374 try
375 {
376 Constructor<?> taskBuilder = taskClass.getConstructor(File.class,
377 File.class);
378 task = (ProgramTask) taskBuilder.newInstance(inputFile,workDir);
379 } catch (Exception e)
380 {
381 reportError("Could not create a program task for "
382 + taskClass.getSimpleName() + DENOPTIMConstants.EOL
383 + ". Details: " + DENOPTIMConstants.EOL
385 }
386
387 try
388 {
390 } catch (InterruptedException e)
391 {
392 e.printStackTrace();
393 } catch (ExecutionException e)
394 {
395 e.printStackTrace();
396 }
397 }
398
399//------------------------------------------------------------------------------
400
406 private static Behavior ensureFileExistsAndIsReadable(File file,
407 Behavior behavior)
408 {
409 if (!file.exists())
410 {
411 return new Behavior(behavior.runType, behavior.cmd, 1, null,
412 "File '"+ file.getAbsolutePath() +"' not found.");
413 }
415 try {
416 format = FileUtils.detectFileFormat(file);
417 } catch (Throwable t) {
418 // Ignore: if anything goes wrong, will still get UNRECOGNIZED which
419 // triggers the error.
420 }
421 if (FileFormat.UNRECOGNIZED.equals(format))
422 {
423 return new Behavior(behavior.runType, behavior.cmd, 1, null,
424 "Could not open file '" + file.getAbsolutePath()
425 + "' because its format is not recognized.");
426 }
427 return behavior;
428 }
429
430//------------------------------------------------------------------------------
431
439 protected static Behavior defineProgramBehavior(String[] args)
440 {
441 CommandLineParser parser = new DefaultParser();
442 CommandLine cmd = null;
443
444 try {
445 cmd = parser.parse(CLIOptions.getInstance(), args);
446 } catch (ParseException e) {
447 String helpMsg = getHelpString();
448 String errMsg = "Unable to parse command-line arguments. "
449 + "Please, check your input! " + DENOPTIMConstants.EOL
450 + "Details: " + e.getMessage();
451 return new Behavior(null, null, 1, helpMsg, errMsg);
452 }
453
454 if (cmd.hasOption(CLIOptions.help))
455 {
456 String helpMsg = getHelpString();
457 return new Behavior(RunType.DRY, null, 0, helpMsg, null);
458 }
459
460 if (cmd.hasOption(CLIOptions.version))
461 {
462 return new Behavior(RunType.DRY, null, 0, Version.VERSION, null);
463 }
464
465 Behavior result = null;
466 if (cmd.getOptions().length==0)
467 result = new Behavior(RunType.GUI, cmd, 0, null, null);
468 else {
469 if (cmd.hasOption(CLIOptions.run))
470 {
471 String rts = cmd.getOptionValue(CLIOptions.run).toString();
472 RunType rt = null;
473 try
474 {
475 rt = RunType.valueOf(rts.toUpperCase());
476 } catch (Exception e)
477 {
478 String errMsg = "Unacceptable value for "
479 + CLIOptions.run.getLongOpt() + " option: "
480 + "'" + rts + "'. Please, modify "
481 + "your command. ";
482 return new Behavior(null, null, 1, null, errMsg);
483 }
484
485 if (rt.isCLIEnabled())
486 {
487 result = new Behavior(rt, cmd);
488 } else {
489 result = new Behavior(null, null, 1, null,
490 "RunType '"+rt+"' is not enabled from CLI. "
491 + "Please, contacts the developers.");
492 }
493 } else {
494 reportError("Command line has no " + CLIOptions.run + " option, "
495 + "but more than zero options. Please, contact the "
496 + "developers.", 1);
497 }
498 }
499
500 List<String> inputFiles = cmd.getArgList();
501 for (String pathname : inputFiles)
502 result = ensureFileExistsAndIsReadable(new File(pathname), result);
503
504 return result;
505 }
506
507//------------------------------------------------------------------------------
508
513 private static String getHelpString()
514 {
515 HelpFormatter formatter = new HelpFormatter();
516 StringWriter out = new StringWriter();
517 PrintWriter pw = new PrintWriter(out);
518
519 formatter.printHelp(pw, 80, "denoptim", DENOPTIMConstants.EOL,
521 formatter.getLeftPadding(), formatter.getDescPadding(),
523 "Run without arguments to launch the graphical user "
524 + "interface (GUI) without opening any file. ", true);
525 pw.flush();
526 return out.toString();
527 }
528
529//------------------------------------------------------------------------------
530
538 private static void reportError(String msg, int exitCode)
539 {
540 System.out.println(DENOPTIMConstants.EOL
541 + "ERROR! " + msg
543 System.exit(exitCode);
544 }
545
546//------------------------------------------------------------------------------
547
552 private static void reportError(Behavior behavior)
553 {
554 if (behavior.helpMsg!=null)
555 System.out.println(behavior.helpMsg);
556 reportError(behavior.errorMsg, behavior.exitStatus);
557 }
558
559//------------------------------------------------------------------------------
560
564 private static void terminate()
565 {
566 try {
568 } catch (Exception e) {
569 e.printStackTrace();
570 System.out.println("StaticTaskManager had problems stopping. "
571 + "Forcing termination.");
572 }
573 Runtime.getRuntime().halt(0);
574 }
575
576//------------------------------------------------------------------------------
577
578}
General set of constants used in DENOPTIM.
static final String EOL
new line character
static String getStackTraceAsString(Throwable t)
Prints the stack trace of an exception into a string.
static FileFormat detectFileFormat(File inFile)
Inspects a file/folder and tries to detect if there is one of the data sources that is recognized by ...
Definition: FileUtils.java:399
Graphical User Interface of the DENOPTIM package.
Definition: GUI.java:57
A tool that start a Py4J gateway server that can listens to calls from Python and translate JAVA obje...
static void launch()
Starts a gateway server using this class as entry point, which then becomes the interpreter of any da...
Class handling DENOPTIM's version identifier for headers.
Definition: Version.java:36
static final String VERSION
Version identifier (from pom.xml via Maven properties)
Definition: Version.java:48
Represents the behavior of the program at start-up.
Definition: Behavior.java:31
RunType runType
The type of run that is requested.
Definition: Behavior.java:51
String errorMsg
The error message.
Definition: Behavior.java:40
CommandLine cmd
The parsed command line arguments.
Definition: Behavior.java:56
int exitStatus
A non-zero value means some error has occurred and the program will terminate.
Definition: Behavior.java:46
String helpMsg
The help message.
Definition: Behavior.java:35
static Option help
Option requesting the printing of the help message.
Definition: CLIOptions.java:37
static CLIOptions getInstance()
Gets the singleton instance of this class.
Definition: CLIOptions.java:79
static Option version
Option requesting only the printing of the version.
Definition: CLIOptions.java:42
static Option run
Option controlling the type of run.
Definition: CLIOptions.java:47
Entry point of any kind of run of the DENOPTIM program.
Definition: Main.java:58
static void reportError(Behavior behavior)
Prints an error message on standard output, possibly after the help message, and then stops the Virtu...
Definition: Main.java:552
static Behavior ensureFileExistsAndIsReadable(File file, Behavior behavior)
Checks the file exists and format is recognized.
Definition: Main.java:406
static String getHelpString()
Prints the help message on a string.
Definition: Main.java:513
static void main(String[] args)
Launches the appropriate program according to the arguments given.
Definition: Main.java:276
static void terminate()
Stops services and halts the Virtual Machine.
Definition: Main.java:564
static void runProgramTask(Class<?> taskClass, File inputFile, File workDir)
Creates a task for the given class.
Definition: Main.java:362
static void reportError(String msg, int exitCode)
Prints an error message on standard output and then stops the Virtual Machine with an exit code.
Definition: Main.java:538
static Behavior defineProgramBehavior(String[] args)
Does the processing of the application arguments and decides what the program should do.
Definition: Main.java:439
Combinatorial exploration of the fragment space.
Programs that runs de novo design by a genetic algorithm.
Definition: GARunner.java:40
Tool to create fragments by chopping 2D/3D chemical structures.
Definition: Fragmenter.java:39
Tool to run genetic operations in a stand-alone fashion, i.e., outside of a genetic algorithm run.
Tool to perform isomorphism analysis on DGraphs.
Task structure for any of the main programs in the denoptim project, such as genetic algorithm and co...
Manager for tasks submitted by the GUI.
static void submitAndWait(Task task)
Submits a task and waits for completion.
static StaticTaskManager getInstance()
Gets the singleton instance of this class.
File formats identified by DENOPTIM.
Definition: FileFormat.java:32
Types of runs that can be requested to the DENOPTIM Main class.
Definition: Main.java:62
String description
A short description of the run type.
Definition: Main.java:142
Class<?> programTaskImpl
The implementation of ProgramTask capable of this run type.
Definition: Main.java:137
CLG
Run a comparison of lists of graphs.
Definition: Main.java:113
GI
Run a stand-alone test for graph isomorphism.
Definition: Main.java:108
B3D
Run the stand-alone conversion of graph into a three-dimensional molecular model.
Definition: Main.java:88
GO
Run a stand-alone genetic operation.
Definition: Main.java:98
GE
Run a stand-alone graph editing task.
Definition: Main.java:103
GA
Run the genetic algorithm with DenoptimGA.
Definition: Main.java:71
GUI
Launch the graphical user interface denoptim.gui.GUI.
Definition: Main.java:66
FIT
stand-alone fitness evaluation
Definition: Main.java:82
boolean needsInputFile
Flag indicating if this run type needs any input file when called from CLI.
Definition: Main.java:153
boolean isCLIEnabled()
Returns true if this run type can be requested from the CLI.
Definition: Main.java:251
PY4J
Starts a listener to Python;.
Definition: Main.java:123
Class<?> getProgramTaskImpl()
Returns the class that implements the program with this type of run.
Definition: Main.java:214
FRG
Run a fragmentation task.
Definition: Main.java:118
static String getRunTypesForUser()
Returns a string that contains a textual list (e.g., "A, B, and C") of the possible types that can be...
Definition: Main.java:224
DRY
Only prints help or version and close program.
Definition: Main.java:93
FSE
Run a combinatorial generation of candidates with FragSpaceExplorer.
Definition: Main.java:77
boolean needsInputFile()
Returns true if this run type needs an input file when requested from CLI.
Definition: Main.java:262
boolean isCLIEnabled
Flag indicating if this run type can be called from CLI.
Definition: Main.java:147