Skip to content

Commit ff9b1c6

Browse files
committed
add build for runnable jar, and improve log4j2 init for that
1 parent 1945abb commit ff9b1c6

File tree

2 files changed

+206
-106
lines changed

2 files changed

+206
-106
lines changed

pom.xml

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,89 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4-
<modelVersion>4.0.0</modelVersion>
2+
<project
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
6+
>
7+
<modelVersion>4.0.0</modelVersion>
58

6-
<name>Java Math Library (JML)</name>
7-
<groupId>de.tilman_neumann</groupId>
8-
<artifactId>jml</artifactId>
9-
<version>1.4.0-alpha.3</version>
10-
<packaging>jar</packaging>
11-
<description>My general math library, containing pretty fast factoring algorithms (except NFS), partition generators and more.</description>
12-
<url>https://github.com/TilmanNeumann/java-math-library</url>
9+
<name>Java Math Library (JML)</name>
10+
<groupId>de.tilman_neumann</groupId>
11+
<artifactId>jml</artifactId>
12+
<version>1.4.0-beta</version>
13+
<packaging>jar</packaging>
14+
<description>My general math library, containing pretty fast factoring algorithms (except NFS), partition generators and more.</description>
15+
<url>https://github.com/TilmanNeumann/java-math-library</url>
1316

14-
<licenses>
15-
<license>
16-
<name>GNU General Public License 3</name>
17-
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
18-
</license>
19-
</licenses>
17+
<licenses>
18+
<license>
19+
<name>GNU General Public License 3</name>
20+
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
21+
</license>
22+
</licenses>
2023

21-
<developers>
22-
<developer>
23-
<name>Tilman Neumann</name>
24-
<email>tilman.neumann@web.de</email>
25-
</developer>
26-
</developers>
24+
<developers>
25+
<developer>
26+
<name>Tilman Neumann</name>
27+
<email>tilman.neumann@web.de</email>
28+
</developer>
29+
</developers>
2730

28-
<dependencies>
29-
<dependency>
30-
<groupId>org.apache.logging.log4j</groupId>
31-
<artifactId>log4j-api</artifactId>
32-
<version>2.24.3</version>
33-
</dependency>
34-
<dependency>
35-
<groupId>org.apache.logging.log4j</groupId>
36-
<artifactId>log4j-core</artifactId>
37-
<version>2.24.3</version>
38-
</dependency>
39-
<dependency>
40-
<groupId>junit</groupId>
41-
<artifactId>junit</artifactId>
42-
<version>4.13.2</version>
43-
<scope>test</scope>
44-
</dependency>
45-
</dependencies>
46-
47-
<build>
48-
<plugins>
49-
<plugin>
50-
<version>3.13.0</version>
51-
<groupId>org.apache.maven.plugins</groupId>
52-
<artifactId>maven-compiler-plugin</artifactId>
53-
<configuration>
54-
<!-- The project still builds with Java 1.8, but with 10 we have better intrinsics -->
55-
<source>10</source>
56-
<target>10</target>
57-
</configuration>
58-
</plugin>
59-
</plugins>
60-
</build>
31+
<dependencies>
32+
<dependency>
33+
<groupId>org.apache.logging.log4j</groupId>
34+
<artifactId>log4j-api</artifactId>
35+
<version>2.24.3</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.apache.logging.log4j</groupId>
39+
<artifactId>log4j-core</artifactId>
40+
<version>2.24.3</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>junit</groupId>
44+
<artifactId>junit</artifactId>
45+
<version>4.13.2</version>
46+
<scope>test</scope>
47+
</dependency>
48+
</dependencies>
6149

50+
<build>
51+
<plugins>
52+
<!-- build library -->
53+
<plugin>
54+
<version>3.13.0</version>
55+
<groupId>org.apache.maven.plugins</groupId>
56+
<artifactId>maven-compiler-plugin</artifactId>
57+
<configuration>
58+
<!-- The project still builds with Java 1.8, but with 10 we have better intrinsics -->
59+
<source>10</source>
60+
<target>10</target>
61+
</configuration>
62+
</plugin>
63+
<!-- build executable jar including all required dependencies -->
64+
<plugin>
65+
<version>3.7.1</version>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-assembly-plugin</artifactId>
68+
<executions>
69+
<execution>
70+
<phase>package</phase>
71+
<goals>
72+
<goal>single</goal>
73+
</goals>
74+
<configuration>
75+
<archive>
76+
<manifest>
77+
<mainClass>de.tilman_neumann.jml.factor.CombinedFactorAlgorithm</mainClass>
78+
</manifest>
79+
</archive>
80+
<descriptorRefs>
81+
<descriptorRef>jar-with-dependencies</descriptorRef>
82+
</descriptorRefs>
83+
</configuration>
84+
</execution>
85+
</executions>
86+
</plugin>
87+
</plugins>
88+
</build>
6289
</project>
Lines changed: 124 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
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.
@@ -15,7 +15,6 @@
1515

1616
import java.io.File;
1717
import java.net.URL;
18-
import java.util.Objects;
1918

2019
import org.apache.logging.log4j.Logger;
2120
import org.apache.logging.log4j.LogManager;
@@ -35,29 +34,46 @@
3534
*/
3635
public 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

Comments
 (0)