This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 355358ea19353ae675bdbf2f8c9f722aa99bc45c
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Dec 1 18:50:32 2023 +0100

    Make possible to force logging configuration after JVM startup.
    This is useful when setting system properties with JVM flags does not work.
    It seems to be the case with jshell for instance.
---
 .../main/org/apache/sis/console/Command.java       | 95 +++++++++++++++++-----
 .../main/org/apache/sis/console/CommandRunner.java |  2 +-
 .../apache/sis/console/ResourcesDownloader.java    |  2 +-
 .../org/apache/sis/util/logging/Initializer.java   | 70 ++++++++++------
 optional/src/org.apache.sis.gui/bundle/bin/sis     |  1 +
 optional/src/org.apache.sis.gui/bundle/bin/sisfx   |  1 +
 .../bundle/conf/logging.properties                 |  6 +-
 7 files changed, 128 insertions(+), 49 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
index 5c1bde2fcd..d2033feca6 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
@@ -18,16 +18,20 @@ package org.apache.sis.console;
 
 import java.util.Locale;
 import java.util.logging.LogManager;
-import java.util.logging.ConsoleHandler;
 import java.io.Console;
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.sql.SQLException;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.internal.X364;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.logging.Initializer;
 import org.apache.sis.util.logging.MonolineFormatter;
 
 
@@ -133,7 +137,7 @@ public final class Command {
      * @throws InvalidCommandException if an invalid command has been given.
      * @throws InvalidOptionException if the given arguments contain an 
invalid option.
      */
-    protected Command(final Object[] args) throws InvalidCommandException, 
InvalidOptionException {
+    public Command(final Object[] args) throws InvalidCommandException, 
InvalidOptionException {
         int commandIndex = -1;
         String commandName = null;
         for (int i=0; i<args.length; i++) {
@@ -171,7 +175,70 @@ public final class Command {
                             Errors.Keys.UnknownCommand_1, commandName), 
commandName);
             }
         }
-        CommandRunner.instance = command;       // For ResourcesDownloader 
only.
+    }
+
+    /**
+     * Loads the logging configuration file if not already done, then 
configures the monoline formatter.
+     * This method performs two main tasks:
+     *
+     * <ol>
+     *   <li>If the {@value Initializer#CONFIG_FILE_PROPERTY} is <em>not</em> 
set, then try
+     *       to set it to {@code $SIS_HOME/conf/logging.properties} and load 
that file.</li>
+     *   <li>If the {@code "derby.stream.error.file"} system property is not 
defined,
+     *       then try to set it to {@code $SIS_HOME/log/derby.log}.</li>
+     *   <li>If the configuration file declares {@link MonolineFormatter} as 
the console formatter,
+     *       ensures that the formatter is loaded and resets its colors 
depending on whether X364
+     *       seems to be supported.</li>
+     * </ol>
+     *
+     * This method can be invoked at initialization time,
+     * such as the beginning of {@code main(…)} static method.
+     *
+     * @since 1.5
+     */
+    public static void configureLogging() {
+        final String value = System.getenv("SIS_HOME");
+        if (value != null) {
+            final Path home = Path.of(value).normalize();
+            Path file = home.resolve("log");
+            if (Files.isDirectory(file)) {
+                setPropertyIfAbsent("derby.stream.error.file", 
file.resolve("derby.log"));
+            }
+            file = home.resolve("conf").resolve("logging.properties");
+            if (Files.isRegularFile(file)) {
+                if (setPropertyIfAbsent(Initializer.CONFIG_FILE_PROPERTY, 
file)) try {
+                    Initializer.reload(file);
+                } catch (IOException e) {
+                    Logging.unexpectedException(null, Command.class, 
"configureLogging", e);
+                }
+            }
+        }
+        /*
+         * The logging configuration is given by the "conf/logging.properties" 
file in the Apache SIS
+         * installation directory. By default, that configuration file 
contains the following line:
+         *
+         *     java.util.logging.ConsoleHandler.formatter = 
org.apache.sis.util.logging.MonolineFormatter
+         *
+         * However, this configuration is sometime silently ignored by the 
LogManager at JVM startup time,
+         * maybe because the Apache SIS classes were not yet loaded. So we 
check again if the configuration
+         * contained that line, and manually re-install the log formatter if 
that line is present.
+         */
+        final String handler = 
LogManager.getLogManager().getProperty("java.util.logging.ConsoleHandler.formatter");
+        if (MonolineFormatter.class.getName().equals(handler)) {
+            
MonolineFormatter.install().resetLevelColors(X364.isAnsiSupported());
+        }
+    }
+
+    /**
+     * Sets the specified system property if that property is not already set.
+     * This method returns whether the property has been set.
+     */
+    private static boolean setPropertyIfAbsent(final String property, final 
Path value) {
+        if (System.getProperty(property) == null) {
+            System.setProperty(property, value.toString());
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -190,12 +257,15 @@ public final class Command {
         if (command.options.containsKey(Option.HELP)) {
             command.help(command.commandName.toLowerCase(Locale.US));
         } else try {
+            CommandRunner.instance.set(command);        // For 
ResourcesDownloader only.
             int status = command.run();
             command.flush();
             return status;
         } catch (Exception e) {
             command.error(null, e);
             throw e;
+        } finally {
+            CommandRunner.instance.remove();
         }
         return 0;
     }
@@ -254,24 +324,7 @@ public final class Command {
      * @param  args  command-line options.
      */
     public static void main(final String[] args) {
-        /*
-         * The logging configuration is given by the "conf/logging.properties" 
file in the Apache SIS
-         * installation directory. By default, that configuration file 
contains the following line:
-         *
-         *     java.util.logging.ConsoleHandler.formatter = 
org.apache.sis.util.logging.MonolineFormatter
-         *
-         * However, this configuration is silently ignored by LogManager at 
JVM startup time, probably
-         * because the Apache SIS class is not on the system module path. So 
we check again for this
-         * configuration line here, and manually install our log formatter 
only if the above-cited
-         * line is present.
-         */
-        final LogManager manager = LogManager.getLogManager();
-        if 
(MonolineFormatter.class.getName().equals(manager.getProperty(ConsoleHandler.class.getName()
 + ".formatter"))) {
-            MonolineFormatter.install();
-        }
-        /*
-         * Now run the command.
-         */
+        configureLogging();
         final Command c;
         try {
             c = new Command(args);
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
index 44bdc42425..5a5aa9f9b5 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
@@ -55,7 +55,7 @@ abstract class CommandRunner {
      * We use this static field as a workaround for the fact that {@code 
ResourcesDownloader} is not
      * instantiated by us, so we cannot pass the {@code CommandRunner} 
instance to its constructor.
      */
-    static CommandRunner instance;
+    static final ThreadLocal<CommandRunner> instance = new ThreadLocal<>();
 
     /**
      * The name of this command, as specified by the user on the command-line.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
index 9ceaf56f09..9ccedb690f 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
@@ -68,7 +68,7 @@ public class ResourcesDownloader extends 
OptionalInstallations {
      */
     public ResourcesDownloader() {
         super("text/plain");
-        final CommandRunner command = CommandRunner.instance;
+        final CommandRunner command = CommandRunner.instance.get();
         if (command != null) {
             locale = command.locale;
             colors = command.colors;
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Initializer.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Initializer.java
index 45e3d0bad0..5c2f11e9d8 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Initializer.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/Initializer.java
@@ -59,8 +59,8 @@ import java.nio.file.Path;
  * (i.e. as documented by {@link FileHandler}).
  *
  * <h2>Usage</h2>
- * This class should not referenced directly by other Java code.
- * Instead, it should be specified at JVM startup time like below:
+ * This class should not be referenced directly by other Java code.
+ * Instead, it can be specified at JVM startup time like below:
  *
  * <pre>java 
-Djava.util.logging.config.class=org.apache.sis.util.logging.Initializer \
  *     
-Djava.util.logging.config.file=<i>path/to/my/application/conf/logging.properties</i></pre>
@@ -68,7 +68,7 @@ import java.nio.file.Path;
  * See for example the {@code bin/sis} shell script in Apache SIS binary 
distribution.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.5
  *
  * @see FileHandler
  *
@@ -80,10 +80,18 @@ public class Initializer {
      *          because it may be invoked early while the application is still 
initializing.
      */
 
+    /**
+     * The system property for the logging configuration file.
+     * The value is defined by {@link LogManager} to {@value}.
+     *
+     * @since 1.5
+     */
+    public static final String CONFIG_FILE_PROPERTY = 
"java.util.logging.config.file";
+
     /**
      * The property for which to replace the {@value #PATTERN} string.
      */
-    private static final String PROPERTY = 
"java.util.logging.FileHandler.pattern";
+    private static final String PATTERN_PROPERTY = 
"java.util.logging.FileHandler.pattern";
 
     /**
      * The pattern to replace.
@@ -93,7 +101,7 @@ public class Initializer {
     /**
      * Configures Java logging using a filtered configuration file.
      * This constructor gets the configuration file referenced by
-     * the {@code "java.util.logging.config.file"} system property,
+     * the {@value #CONFIG_FILE_PROPERTY} system property,
      * applies the filtering described in class javadoc,
      * then gives the filtered configuration to {@link 
LogManager#readConfiguration(InputStream)}.
      *
@@ -103,29 +111,45 @@ public class Initializer {
      * @throws IOException if an error occurred while reading the 
configuration file.
      */
     public Initializer() throws IOException {
-        final String file = 
System.getProperty("java.util.logging.config.file");
+        final String file = System.getProperty(CONFIG_FILE_PROPERTY);
         if (file != null) {
-            final Path path = Path.of(file).normalize();
-            final StringBuilder buffer = new StringBuilder(600);
-            for (String line : Files.readAllLines(path)) {
-                if (!(line = line.trim()).isEmpty() && line.charAt(0) != '#') {
-                    final int base = buffer.length();
-                    buffer.append(line).append('\n');
-                    if (line.startsWith(PROPERTY)) {
-                        final int i = buffer.indexOf(PATTERN, base + 
PROPERTY.length());
-                        if (i >= 0) {
-                            Path parent = path;
-                            for (int j=Math.min(parent.getNameCount(), 2); --j 
>= 0;) {
-                                parent = parent.getParent();
-                            }
-                            String replacement = (parent != null) ? 
parent.toString() : ".";
-                            replacement = 
replacement.replace(File.separatorChar, '/');
-                            buffer.replace(i, i + PATTERN.length(), 
replacement);
+            reload(Path.of(file));
+        }
+    }
+
+    /**
+     * Reloads the logging configuration from the specified file.
+     * The new configuration replaces the previous one (this is not an update).
+     * The extended pattern ({@code "%p"}) is parsed as described in the class 
summary.
+     *
+     * @param  path  path to the logging configuration file.
+     * @throws IOException if an error occurred while reading the 
configuration file.
+     *
+     * @see LogManager#readConfiguration(InputStream)
+     *
+     * @since 1.5
+     */
+    public static void reload(Path path) throws IOException {
+        path = path.normalize();
+        final StringBuilder buffer = new StringBuilder(600);
+        for (String line : Files.readAllLines(path)) {
+            if (!(line = line.trim()).isEmpty() && line.charAt(0) != '#') {
+                final int base = buffer.length();
+                buffer.append(line).append('\n');
+                if (line.startsWith(PATTERN_PROPERTY)) {
+                    final int i = buffer.indexOf(PATTERN, base + 
PATTERN_PROPERTY.length());
+                    if (i >= 0) {
+                        Path parent = path;
+                        for (int j=Math.min(parent.getNameCount(), 2); --j >= 
0;) {
+                            parent = parent.getParent();
                         }
+                        String replacement = (parent != null) ? 
parent.toString() : ".";
+                        replacement = replacement.replace(File.separatorChar, 
'/');
+                        buffer.replace(i, i + PATTERN.length(), replacement);
                     }
                 }
             }
-            LogManager.getLogManager().readConfiguration(new 
ByteArrayInputStream(buffer.toString().getBytes()));
         }
+        LogManager.getLogManager().readConfiguration(new 
ByteArrayInputStream(buffer.toString().getBytes()));
     }
 }
diff --git a/optional/src/org.apache.sis.gui/bundle/bin/sis 
b/optional/src/org.apache.sis.gui/bundle/bin/sis
index 1caa9c6359..b3f310a39a 100755
--- a/optional/src/org.apache.sis.gui/bundle/bin/sis
+++ b/optional/src/org.apache.sis.gui/bundle/bin/sis
@@ -21,6 +21,7 @@ set -o errexit
 BASE_DIR="`dirname "$( readlink -e "$0"; )";`/.."
 SIS_DATA="${SIS_DATA:-$BASE_DIR/data}"
 export SIS_DATA
+unset  SIS_HOME
 
 # Execute SIS with any optional JAR that the user may put in the 'lib' 
directory.
 java --module-path 
"$BASE_DIR/lib:$BASE_DIR/lib/app/org.apache.sis.console.jar" \
diff --git a/optional/src/org.apache.sis.gui/bundle/bin/sisfx 
b/optional/src/org.apache.sis.gui/bundle/bin/sisfx
index 4bbdfcd5b5..68a85b4ff9 100755
--- a/optional/src/org.apache.sis.gui/bundle/bin/sisfx
+++ b/optional/src/org.apache.sis.gui/bundle/bin/sisfx
@@ -23,6 +23,7 @@ BASE_DIR="`dirname "$( readlink -e "$0"; )";`/.."
 
 SIS_DATA="${SIS_DATA:-$BASE_DIR/data}"
 export SIS_DATA
+unset  SIS_HOME
 
 if [ -z "$PATH_TO_FX" ]
 then
diff --git a/optional/src/org.apache.sis.gui/bundle/conf/logging.properties 
b/optional/src/org.apache.sis.gui/bundle/conf/logging.properties
index 95b2fa71fa..d65c2a4940 100644
--- a/optional/src/org.apache.sis.gui/bundle/conf/logging.properties
+++ b/optional/src/org.apache.sis.gui/bundle/conf/logging.properties
@@ -35,7 +35,7 @@ handlers = java.util.logging.FileHandler, \
 #   - By handler (INFO for console, FINE for log file).
 
 .level = CONFIG
-org.apache.sis.level = FINER
+org.apache.sis.level = FINE
 java.util.logging.FileHandler.level = FINE
 java.util.logging.ConsoleHandler.level = INFO
 
@@ -63,6 +63,6 @@ java.util.logging.ConsoleHandler.level = INFO
 #         "class:short", "class:long" and "class.method".
 
 java.util.logging.FileHandler.pattern      = %p/log/sis.log
-java.util.logging.FileHandler.formatter    = 
org.apache.sis.util.logging.MonolineFormatter
+java.util.logging.FileHandler.formatter    = java.util.logging.SimpleFormatter
 java.util.logging.ConsoleHandler.formatter = 
org.apache.sis.util.logging.MonolineFormatter
-org.apache.sis.util.logging.MonolineFormatter.source = logger:long
+org.apache.sis.util.logging.MonolineFormatter.source = class:short

Reply via email to