11/*
22 * java-math-library is a Java library focused on number theory, but not necessarily limited to it. It is based on the PSIQS 4.0 factoring project.
3- * Copyright (C) 2018 Tilman Neumann - tilman.neumann@web.de
3+ * Copyright (C) 2018-2024 Tilman Neumann - tilman.neumann@web.de
44 *
55 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
66 * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
1515
1616import java .io .File ;
1717import java .net .URL ;
18- import java .util .Objects ;
1918
2019import org .apache .logging .log4j .Logger ;
2120import org .apache .logging .log4j .LogManager ;
3534 */
3635public class ConfigUtil {
3736
37+ @ SuppressWarnings ("unused" )
3838 private static final Logger LOG = LogManager .getLogger (ConfigUtil .class );
3939
4040 private static boolean initialized = false ;
4141
4242 /** File separator used on this OS. */
4343 public static String FILE_SEPARATOR ;
44+
4445 /** Path separator used on this OS. */
4546 public static String PATH_SEPARATOR ;
46- /** The root directory of the current project. */
47+
48+ /**
49+ * The root directory of the current project, derived from the system property 'user.dir'. This is<br>
50+ * a) the jml project root directory if jml is run as a project<br>
51+ * b) the project root directory of a custom project using jml as a jar<br>
52+ * c) the folder containing the jar file if jml is run as a runnable jar<br>
53+ */
4754 public static String PROJECT_ROOT ;
48- /** The base folder for all configuration files in this project. */
55+
56+ /** The folder for configuration files in this project. */
4957 public static String CONF_ROOT ;
58+
5059 /** Java class path */
5160 public static String JAVA_CLASS_PATH ;
61+
5262 /** Java library path */
5363 public static String JAVA_LIBRARY_PATH ;
64+
5465 /** Java temp directory */
5566 public static String JAVA_TMP_DIR ;
67+
5668 /** user home directory */
5769 public static String USER_HOME ;
58-
70+
5971 /** number of processors to use for parallel implementations */
6072 public static int NUMBER_OF_PROCESSORS ;
73+
74+ /** if true, then jml is deployed as a (possibly executable) jar file; otherwise as a Java project. */
75+ public static boolean IS_DEPLOYED_AS_JAR ;
76+
6177
6278 private ConfigUtil () {
6379 // static class
@@ -83,11 +99,6 @@ public static void initProject(boolean verbose) {
8399 if (verbose ) System .out .println ("system file separator = " + FILE_SEPARATOR );
84100 PATH_SEPARATOR = System .getProperty ("path.separator" );
85101 if (verbose ) System .out .println ("system path separator = " + PATH_SEPARATOR );
86- // user.dir is
87- // * the jml project root directory if jml is run as a project
88- // * the project root directory of a custom project using jml as a jar
89- // * the folder containing the jar file if jml is run as a runnable jar
90- // So this is a good place to put the data and configuration files required by jml
91102 PROJECT_ROOT = System .getProperty ("user.dir" );
92103 if (verbose ) System .out .println ("project root directory (user.dir) = " + PROJECT_ROOT );
93104 CONF_ROOT = PROJECT_ROOT + FILE_SEPARATOR + "conf" ;
@@ -102,48 +113,12 @@ public static void initProject(boolean verbose) {
102113 if (verbose ) System .out .println ("user.home = " + USER_HOME );
103114 NUMBER_OF_PROCESSORS = Runtime .getRuntime ().availableProcessors ();
104115 if (verbose ) System .out .println ("number of processors = " + NUMBER_OF_PROCESSORS );
116+ IS_DEPLOYED_AS_JAR = "jar" .equals (getClassResourceProtocol (verbose ));
117+ if (verbose ) System .out .println ("jml deployed as jar = " + IS_DEPLOYED_AS_JAR );
105118
106- String confFileStr = CONF_ROOT + FILE_SEPARATOR + "log4j2-test.xml" ;
107- File confFile = new File (confFileStr );
108- if (confFile .exists ()) {
109- // initialize Log4j from xml configuration
110- //DOMConfigurator.configure(confFileStr);
111- System .setProperty ("log4j.configurationFile" , confFileStr );
112- Configurator .reconfigure (); // TODO do we need this?
113- if (verbose ) LOG .info ("log4j configuration successfully loaded from file " + confFileStr + "." );
114- if (verbose ) LOG .info ("project initialization finished." );
115- initialized = true ;
116- return ;
117- }
118-
119- // Not finding the log4j config file would be bad when not run from runnable jar
120- boolean runningFromJar = Objects .equals (getClassResourceProtocol (verbose ), "rsrc" );
121- if (!runningFromJar ) {
122- System .err .println ("WARNING: Unable to find log4j configuration file " + confFileStr + "." );
123- System .err .println ("WARNING: An emergency logger with limited capabilities will be used." );
124- System .err .println ("WARNING: Please put a proper log4jconf.xml file into the designated folder if this message is annoying you..." );
125- }
126-
127- // Create default logger, following https://www.baeldung.com/log4j2-programmatic-config
128- ConfigurationBuilder <BuiltConfiguration > builder = ConfigurationBuilderFactory .newConfigurationBuilder ();
129- // create appenders to console and log.txt
130- AppenderComponentBuilder console = builder .newAppender ("stdout" , "Console" );
131- builder .add (console );
132- AppenderComponentBuilder file = builder .newAppender ("log" , "File" );
133- file .addAttribute ("fileName" , "log.txt" );
134- builder .add (file );
135- // declare layouts
136- LayoutComponentBuilder standard = builder .newLayout ("PatternLayout" );
137- standard .addAttribute ("pattern" , "%d [%t] %-5level: %msg%n%throwable" );
138- console .add (standard );
139- file .add (standard );
140- // set up root logger with debug level
141- RootLoggerComponentBuilder rootLogger = builder .newRootLogger (Level .DEBUG );
142- rootLogger .add (builder .newAppenderRef ("stdout" ));
143- builder .add (rootLogger );
144- // let it be used
145- Configurator .initialize (builder .build ());
146- LOG .info ("Created default logger:\n " + builder .toXmlConfiguration ());
119+ setupLog4j2 (verbose );
120+
121+ if (verbose ) System .out .println ("project initialization finished." );
147122 initialized = true ;
148123 }
149124
@@ -156,4 +131,102 @@ private static String getClassResourceProtocol(boolean verbose) {
156131 if (verbose ) System .out .println ("protocol = " + protocol );
157132 return protocol ;
158133 }
134+
135+ private static void setupLog4j2 (boolean verbose ) {
136+ // Set up log4j2...
137+ // First choice is any custom configuration file defined by system property LOG4J2_CONFIGURATION_FILE
138+ String log4j2ConfigFile = System .getProperty ("log4j2.configurationFile" );
139+ if (log4j2ConfigFile != null ) {
140+ if (new File (log4j2ConfigFile ).exists ()) {
141+ // the logger should already be initialized correctly, but here we write to stdout because we don't know the log level
142+ if (verbose ) System .out .println ("Using custom log4j2 configuration file " + log4j2ConfigFile + " defined by system property 'log4j2.configurationFile'" );
143+ return ;
144+ } else {
145+ if (verbose ) System .out .println ("The log4j2 configuration file " + log4j2ConfigFile + " defined by system property 'log4j2.configurationFile' does not exist" );
146+ }
147+ } else {
148+ if (verbose ) System .out .println ("The system property 'log4j2.configurationFile' is not defined." );
149+ }
150+
151+ if (IS_DEPLOYED_AS_JAR ) {
152+ // So far I failed to find a way to change the log level in the case that jml is run from a jar.
153+ // Thus the only possible fallback is the default log4j2 console Logger with loglevel ERROR.
154+ // This will be disappointing if it is a runnable jar and we see no input prompt nor factoring outputs...
155+ if (verbose ) {
156+ System .out .println ("jml will use the default log4j2 console Logger with loglevel ERROR." );
157+ System .out .println ("To take control of the logging behavior, define the system property 'log4j2.configurationFile' such that it points to a valid log4j2 configuration file." );
158+ System .out .println ("For a runnable jar, use something like 'java -Dlog4j2.configurationFile=<some-file-path>/log4j2-test.xml -jar jml<version>.jar'" );
159+ }
160+ return ; // initialized, though not perfectly
161+ }
162+
163+ // Now we know that jml is run as a Java project. In that case we have more options...
164+ // 2nd choice is the default log4j2-test.xml in the conf folder of the project.
165+ String defaultConfigFile = CONF_ROOT + FILE_SEPARATOR + "log4j2-test.xml" ;
166+ if (new File (defaultConfigFile ).exists ()) {
167+ // initialize Log4j2 from xml configuration
168+ System .setProperty ("log4j2.configurationFile" , defaultConfigFile );
169+ Configurator .reconfigure ();
170+ if (verbose ) System .out .println ("log4j configuration successfully loaded from file " + defaultConfigFile + "." );
171+ return ;
172+ } else {
173+ if (verbose ) {
174+ System .out .println ("The default configuration file " + defaultConfigFile + " does not exist" );
175+ System .out .println ("An emergency logger will be used that logs to console only." );
176+ }
177+ }
178+
179+ // 3rd choice is to setup a simple emergencey logger.
180+ // For this we could use the log4j2 default logger
181+ //configureDefaultLogger_v1();
182+ // or create one programmatically on our own
183+ configureDefaultLogger_v2 (false );
184+ }
185+
186+ /**
187+ * This version simply changes the log level of the log4j2 default logger.
188+ * The default logger only logs to console and has pattern layout "%d [%t] %-5p %c - %m%n".
189+ *
190+ * Works only when run as a project.
191+ */
192+ private static void configureDefaultLogger_v1 () {
193+ Configurator .setRootLevel (Level .INFO );
194+ }
195+
196+ /**
197+ * Create an emergency logger on our own.
198+ *
199+ * Works only when run as a project.
200+ *
201+ * @param addFileLogger
202+ */
203+ private static void configureDefaultLogger_v2 (boolean addFileLogger ) {
204+ // Create default logger, following https://www.baeldung.com/log4j2-programmatic-config.
205+ // This works only when running from a project.
206+ ConfigurationBuilder <BuiltConfiguration > builder = ConfigurationBuilderFactory .newConfigurationBuilder ();
207+ // create console appenders
208+ AppenderComponentBuilder console = builder .newAppender ("stdout" , "Console" );
209+ builder .add (console );
210+ // add layout
211+ LayoutComponentBuilder consoleLayout = builder .newLayout ("PatternLayout" );
212+ consoleLayout .addAttribute ("pattern" , "%d %-5p %c{1}(%L) [%t]: %m%n" );
213+ console .add (consoleLayout );
214+
215+ if (addFileLogger ) {
216+ AppenderComponentBuilder file = builder .newAppender ("log" , "File" );
217+ file .addAttribute ("fileName" , "log.txt" );
218+ builder .add (file );
219+ LayoutComponentBuilder fileLayout = builder .newLayout ("PatternLayout" );
220+ fileLayout .addAttribute ("pattern" , "%m%n" );
221+ file .add (fileLayout );
222+ }
223+
224+ // set up root logger
225+ RootLoggerComponentBuilder rootLogger = builder .newRootLogger (Level .DEBUG );
226+ rootLogger .add (builder .newAppenderRef ("stdout" ));
227+ builder .add (rootLogger );
228+ // let it be used
229+ Configurator .reconfigure (builder .build ());
230+ }
231+
159232}
0 commit comments