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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new 1e8dc4cba9 More effort in making log messages less numerous and more relevant when the EPSG database is not available. 1e8dc4cba9 is described below commit 1e8dc4cba94b419a85fb2479bf256a20ef687752 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Oct 5 17:21:22 2024 +0200 More effort in making log messages less numerous and more relevant when the EPSG database is not available. --- endorsed/build.gradle.kts | 1 + .../apache/sis/metadata/sql/MetadataSource.java | 3 + .../sis/metadata/sql/privy/ScriptRunner.java | 20 ++---- .../factory/GeodeticAuthorityFactory.java | 4 +- .../sis/referencing/factory/sql/EPSGFactory.java | 13 ++-- .../sis/referencing/factory/sql/EPSGInstaller.java | 19 ++--- .../factory/sql/InstallationScriptProvider.java | 83 +++++++++++++--------- .../referencing/factory/sql/EPSGInstallerTest.java | 2 +- .../org/apache/sis/util/resources/Messages.java | 6 ++ .../apache/sis/util/resources/Messages.properties | 1 + .../sis/util/resources/Messages_fr.properties | 1 + incubator/build.gradle.kts | 1 + .../sis/resources/embedded/EmbeddedResources.java | 3 +- 13 files changed, 92 insertions(+), 65 deletions(-) diff --git a/endorsed/build.gradle.kts b/endorsed/build.gradle.kts index c4252f8581..ffd4adcfd4 100644 --- a/endorsed/build.gradle.kts +++ b/endorsed/build.gradle.kts @@ -206,6 +206,7 @@ tasks.test { events("FAILED", "STANDARD_OUT", "STANDARD_ERROR") setExceptionFormat("FULL") } + systemProperty("java.awt.headless", "true") systemProperty("junit.jupiter.execution.parallel.enabled", "true") systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") systemProperty("junit.jupiter.execution.parallel.mode.classes.default", "concurrent") diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java index ede490d8a9..8f2367c401 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java @@ -374,6 +374,7 @@ public class MetadataSource implements AutoCloseable { { ArgumentChecks.ensureNonNull("standard", standard); ArgumentChecks.ensureNonNull("dataSource", dataSource); + @SuppressWarnings("LocalVariableHidesMemberVariable") ClassLoader classloader; Integer maxStatements; @@ -453,6 +454,7 @@ public class MetadataSource implements AutoCloseable { * @throws SQLException if an error occurred while inserting the metadata. */ final synchronized void install() throws IOException, SQLException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final Connection connection = connection(); final DatabaseMetaData md = connection.getMetaData(); if (md.storesUpperCaseIdentifiers()) { @@ -541,6 +543,7 @@ public class MetadataSource implements AutoCloseable { if (tableName == null) { tableName = getTableName(type); } + @SuppressWarnings("LocalVariableHidesMemberVariable") final SQLBuilder helper = helper(); final String query = helper.clear().append("SELECT * FROM ") .appendIdentifier(schema, tableName).append(" WHERE ") diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/ScriptRunner.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/ScriptRunner.java index b8785b9cce..8697ccaf2d 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/ScriptRunner.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/ScriptRunner.java @@ -35,9 +35,9 @@ import java.sql.Statement; import java.sql.Connection; import java.sql.SQLException; import java.sql.DatabaseMetaData; -import org.apache.sis.util.Debug; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.CharSequences; +import org.apache.sis.util.privy.Strings; import org.apache.sis.util.resources.Errors; @@ -241,21 +241,18 @@ public class ScriptRunner implements AutoCloseable { * Name of the SQL script under execution, or {@code null} if unknown. * This is used only for error reporting. */ - @Debug private String currentFile; /** * The line number of the SQL statement being executed. The first line in a file is numbered 1. * This is used only for error reporting. */ - @Debug private int currentLine; /** * The SQL statement being executed. * This is used only for error reporting. */ - @Debug private String currentSQL; /** @@ -464,7 +461,7 @@ public class ScriptRunner implements AutoCloseable { if (in == null) { throw new FileNotFoundException(Errors.format(Errors.Keys.FileNotFound_1, filename)); } - try (BufferedReader reader = new LineNumberReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + try (var reader = new LineNumberReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { return run(filename, reader); } } @@ -487,7 +484,7 @@ public class ScriptRunner implements AutoCloseable { int statementCount = 0; // For informative purpose only. int posOpeningQuote = -1; // -1 if we are not inside a text. boolean isInsideIdentifier = false; - final StringBuilder buffer = new StringBuilder(); + final var buffer = new StringBuilder(); String line; while ((line = in.readLine()) != null) { /* @@ -812,7 +809,7 @@ parseLine: while (pos < length) { (currentLine != 0) ? currentLine : '?'); } if (currentSQL != null) { - final StringBuilder buffer = new StringBuilder(); + final var buffer = new StringBuilder(); if (position != null) { buffer.append(position).append('\n'); } @@ -822,15 +819,12 @@ parseLine: while (pos < length) { } /** - * Returns a string representation of this runner for debugging purpose. Current implementation returns the - * current position in the script being executed, and the SQL statement. This method may be invoked after a - * {@link SQLException} occurred in order to determine the line in the SQL script that caused the error. + * Returns a string representation of this runner for debugging purpose. * - * @return the current position in the script being executed. + * @return a string representation for debugging purpose. */ - @Debug @Override public String toString() { - return status(null); + return Strings.toString(getClass(), "status", status(null)); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java index e2d7464def..55a356df52 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java @@ -1324,8 +1324,8 @@ public abstract class GeodeticAuthorityFactory extends AbstractFactory implement */ @Override public String toString() { - final StringBuilder buffer = new StringBuilder(Classes.getShortClassName(this)) - .append("[“").append(Citations.getIdentifier(getAuthority())).append('”'); + final var buffer = new StringBuilder(Classes.getShortClassName(this)); + // Do not append `getAuthority()` because it may perform database access. appendStringTo(buffer); return buffer.append(']').toString(); } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGFactory.java index 50fb583cb1..420f8dc5f9 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGFactory.java @@ -380,13 +380,13 @@ public class EPSGFactory extends ConcurrentAuthorityFactory<EPSGDataAccess> impl public synchronized void install(final Connection connection) throws UnavailableFactoryException { String message = null; Exception failure = null; + boolean success = false; try (EPSGInstaller installer = new EPSGInstaller(Objects.requireNonNull(connection))) { final boolean ac = connection.getAutoCommit(); if (ac) { connection.setAutoCommit(false); } try { - boolean success = false; try { if (!"".equals(schema)) { // Schema may be null. installer.setSchema(schema != null ? schema : Constants.EPSG); @@ -394,8 +394,7 @@ public class EPSGFactory extends ConcurrentAuthorityFactory<EPSGDataAccess> impl installer.prependNamespace(catalog); } } - installer.run(scriptProvider, locale); - success = true; + success = installer.run(scriptProvider, locale); } finally { if (ac) { if (success) { @@ -411,10 +410,14 @@ public class EPSGFactory extends ConcurrentAuthorityFactory<EPSGDataAccess> impl failure = e; } } catch (SQLException e) { - message = Messages.forLocale(locale).getString(Messages.Keys.CanNotCreateSchema_1, Constants.EPSG); failure = e; } - if (failure != null) { + if (!success) { + if (message == null) { + message = Messages.forLocale(locale).getString( + (failure != null) ? Messages.Keys.CanNotCreateSchema_1 + : Messages.Keys.NoDataSourceFound_1, Constants.EPSG); + } /* * Derby sometimes wraps SQLException into another SQLException. For making the stack strace a * little bit simpler, keep only the root cause provided that the exception type is compatible. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGInstaller.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGInstaller.java index 23f169f240..9812eaa539 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGInstaller.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGInstaller.java @@ -225,17 +225,21 @@ final class EPSGInstaller extends ScriptRunner { * * @param scriptProvider user-provided scripts, or {@code null} for automatic lookup. * @param locale the locale for information or warning messages, if any. + * @return whether the database has been installed. * @throws FileNotFoundException if a SQL script has not been found. * @throws IOException if another error occurred while reading an input. * @throws SQLException if an error occurred while executing a SQL statement. */ - public void run(InstallationResources scriptProvider, final Locale locale) throws SQLException, IOException { + public boolean run(InstallationResources scriptProvider, final Locale locale) throws SQLException, IOException { long time = System.nanoTime(); - InstallationScriptProvider.log(Messages.forLocale(locale).getLogRecord(Level.INFO, - Messages.Keys.CreatingSchema_2, EPSG, SQLUtilities.getSimplifiedURL(getConnection().getMetaData()))); if (scriptProvider == null) { scriptProvider = lookupProvider(locale); + if (scriptProvider == null) { + return false; + } } + InstallationScriptProvider.log(Messages.forLocale(locale).getLogRecord(Level.INFO, + Messages.Keys.CreatingSchema_2, EPSG, SQLUtilities.getSimplifiedURL(getConnection().getMetaData()))); final String[] scripts = scriptProvider.getResourceNames(EPSG); int numRows = 0; for (int i=0; i<scripts.length; i++) { @@ -247,6 +251,7 @@ final class EPSGInstaller extends ScriptRunner { InstallationScriptProvider.log(Messages.forLocale(locale).getLogRecord( PerformanceLevel.forDuration(time, TimeUnit.NANOSECONDS), Messages.Keys.InsertDuration_2, numRows, time / (float) Constants.NANOS_PER_SECOND)); + return true; } /** @@ -266,6 +271,7 @@ final class EPSGInstaller extends ScriptRunner { * </ol> * * @param locale the locale for information or warning messages, if any. + * @return the SQL preferred script provider, or {@code null} if none. */ private static InstallationResources lookupProvider(final Locale locale) throws IOException { InstallationResources fallback = null; @@ -284,11 +290,8 @@ final class EPSGInstaller extends ScriptRunner { * the user if (s)he accepts EPSG terms of use. But before to use those fallbacks, check if the * data have not been downloaded manually in the "$SIS_DATA/Databases/ExternalSources" directory. */ - final InstallationResources manual = new InstallationScriptProvider.Default(locale); - if (fallback != null && manual.getAuthorities().isEmpty()) { - return fallback; - } - return manual; + final var manual = new InstallationScriptProvider.Default(locale); + return manual.getAuthorities().isEmpty() ? fallback : manual; } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java index 8f6ea07f73..d7265cbb8e 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java @@ -32,6 +32,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.NoSuchFileException; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.logging.Logging; import org.apache.sis.setup.InstallationResources; @@ -62,7 +63,7 @@ import org.apache.sis.util.privy.Constants; * </ol> * * @author Martin Desruisseaux (Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.7 */ public abstract class InstallationScriptProvider extends InstallationResources { @@ -208,16 +209,21 @@ public abstract class InstallationScriptProvider extends InstallationResources { throw new IllegalStateException(Resources.format(Resources.Keys.UnknownAuthority_1, authority)); } String name = resources[resource]; - final InputStream in; + InputStream in; + NoSuchFileException cause = null; if (PREPARE.equals(name) || FINISH.equals(name)) { name = authority + '_' + name + ".sql"; in = InstallationScriptProvider.class.getResourceAsStream(name); - } else { + } else try { in = openStream(name); - name = name.concat(".sql"); + } catch (NoSuchFileException e) { + cause = e; + in = null; } if (in == null) { - throw new FileNotFoundException(Errors.format(Errors.Keys.FileNotFound_1, name)); + var e = new FileNotFoundException(Errors.format(Errors.Keys.FileNotFound_1, name)); + e.initCause(cause); + throw e; } return new LineNumberReader(new InputStreamReader(in, StandardCharsets.UTF_8)); } @@ -267,7 +273,7 @@ public abstract class InstallationScriptProvider extends InstallationResources { /** - * The default implementation which use the scripts in the {@code $SIS_DATA/Databases/ExternalSources} + * The default implementation which uses the scripts in the {@code $SIS_DATA/Databases/ExternalSources} * directory, if present. This class expects the files to have those exact names where {@code *} stands * for any characters provided that there is no ambiguity: * @@ -284,7 +290,7 @@ public abstract class InstallationScriptProvider extends InstallationResources { /** * The directory containing the scripts, or {@code null} if it does not exist. */ - private Path directory; + private final Path directory; /** * Index of the first real file in the array given to the constructor. @@ -305,44 +311,53 @@ public abstract class InstallationScriptProvider extends InstallationResources { "FKeys", FINISH); - Path dir = DataDirectory.DATABASES.getDirectory(); - if (dir != null) { - dir = dir.resolve("ExternalSources"); - if (Files.isDirectory(dir)) { - final String[] resources = super.resources; - final String[] found = new String[resources.length - FIRST_FILE - 1]; - try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "EPSG_*.sql")) { - for (final Path path : stream) { - final String name = path.getFileName().toString(); - for (int i=0; i<found.length; i++) { - final String part = resources[FIRST_FILE + i]; - if (name.contains(part)) { - if (found[i] != null) { - log(Errors.forLocale(locale) - .getLogRecord(Level.WARNING, Errors.Keys.DuplicatedFileReference_1, part)); - return; // Stop the search because of duplicated file. - } + final String[] resources = super.resources; + final String[] found = new String[resources.length - (FIRST_FILE + 1)]; + @SuppressWarnings("LocalVariableHidesMemberVariable") + Path directory = DataDirectory.DATABASES.getDirectory(); + if (directory == null || Files.isDirectory(directory = directory.resolve("ExternalSources"))) { + try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, "EPSG_*.sql")) { + for (final Path path : stream) { + final String name = path.getFileName().toString(); + for (int i=0; i<found.length; i++) { + final String part = resources[FIRST_FILE + i]; + if (name.contains(part)) { + if (found[i] == null) { found[i] = name; + } else { + log(Errors.forLocale(locale) + .getLogRecord(Level.WARNING, Errors.Keys.DuplicatedFileReference_1, part)); } } } } - for (int i=0; i<found.length; i++) { - final String file = found[i]; - if (file != null) { - resources[FIRST_FILE + i] = file; - } else { - dir = null; - } - } - directory = dir; } + } else { + directory = null; + } + this.directory = directory; + /* + * Store the actual file names including the target database and EPSG dataset version. Example: + * + * - PostgreSQL_Table_Script.sql + * - PostgreSQL_Data_Script.sql + * - PostgreSQL_FKey_Script.sql + * + * If a file was not found, use the "EPSG_Foo_Script.sql" name pattern for giving some hints + * to users about which file was expected. This filename will appear in the exception message. + */ + for (int i=0; i<found.length; i++) { + String file = found[i]; + if (file == null) { + file = "EPSG_" + resources[FIRST_FILE + i] + "_Script.sql"; + } + resources[FIRST_FILE + i] = file; } } /** * Returns {@code "EPSG"} if the scripts exist in the {@code ExternalSources} subdirectory, - * or {@code "unavailable"} otherwise. + * or an empty set otherwise. * * @return {@code "EPSG"} if the SQL scripts for installing the EPSG dataset are available, * or an empty set otherwise. diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java index c028bca377..5822892e0a 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGInstallerTest.java @@ -104,7 +104,7 @@ public final class EPSGInstallerTest extends TestCaseWithLogs { * or skip the JUnit test if those scripts are not found. */ private static InstallationScriptProvider getScripts() throws IOException { - final InstallationScriptProvider scripts = new InstallationScriptProvider.Default(null); + final var scripts = new InstallationScriptProvider.Default(null); assumeTrue(scripts.getAuthorities().contains(Constants.EPSG), "EPSG scripts not found in Databases/ExternalSources directory."); return scripts; diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java index f3808051b5..2a661986a5 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java @@ -191,6 +191,12 @@ public class Messages extends IndexedResourceBundle { */ public static final short LocalesDiscarded = 25; + /** + * No source of {0} data has been found. Those data may require an optional module or manual + * installation. + */ + public static final short NoDataSourceFound_1 = 37; + /** * This “{0}” formatting is a departure from standard format. */ diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.properties b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.properties index 4b67fa81ba..d9fe0e2450 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.properties +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.properties @@ -43,6 +43,7 @@ IncompleteParsing_1 = Parsing of \u201c{0}\u201d done, but some ele InsertDuration_2 = Inserted {0} records in {1} seconds. JNDINotSpecified_1 = No object associated to the \u201c{0}\u201d JNDI name. LocalesDiscarded = Text were discarded for some locales. +NoDataSourceFound_1 = No source of {0} data has been found. Those data may require an optional module or manual installation. OptionalModuleNotFound_1 = Optional module \u201c{0}\u201d requested but not found. PossibleInconsistency_1 = Possible inconsistency in \u201c{0}\u201d. PropertyHiddenBy_2 = Property \u201c{0}\u201d is hidden by \u201c{1}\u201d. diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages_fr.properties b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages_fr.properties index 2a30ab9829..ac6e9a2d99 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages_fr.properties +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages_fr.properties @@ -50,6 +50,7 @@ IncompleteParsing_1 = La lecture de \u00ab\u202f{0}\u202f\u00bb a \ InsertDuration_2 = {0} enregistrements ont \u00e9t\u00e9 ajout\u00e9s en {1} secondes. JNDINotSpecified_1 = Aucun objet n\u2019est associ\u00e9 au nom JNDI \u00ab\u202f{0}\u202f\u00bb. LocalesDiscarded = Des textes ont \u00e9t\u00e9 ignor\u00e9s pour certaines langues. +NoDataSourceFound_1 = Aucune source de donn\u00e9es {0} n\u2019a \u00e9t\u00e9 trouv\u00e9e. Ces donn\u00e9es peuvent n\u00e9cessiter un module optionnel ou une installation manuelle. OptionalModuleNotFound_1 = Le module optionnel \u00ab\u202f{0}\u202f\u00bb a \u00e9t\u00e9 demand\u00e9 mais n\u2019a pas \u00e9t\u00e9 trouv\u00e9. PossibleInconsistency_1 = Il y a possiblement une incoh\u00e9rence dans \u00ab\u202f{0}\u202f\u00bb. PropertyHiddenBy_2 = La propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb est masqu\u00e9e par \u00ab\u202f{1}\u202f\u00bb. diff --git a/incubator/build.gradle.kts b/incubator/build.gradle.kts index 85c219f390..36f3496396 100644 --- a/incubator/build.gradle.kts +++ b/incubator/build.gradle.kts @@ -148,6 +148,7 @@ tasks.test { events("FAILED", "STANDARD_OUT", "STANDARD_ERROR") setExceptionFormat("FULL") } + systemProperty("java.awt.headless", "true") systemProperty("junit.jupiter.execution.parallel.enabled", "true") systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") systemProperty("junit.jupiter.execution.parallel.mode.classes.default", "concurrent") diff --git a/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java b/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java index fade9c8e45..c53faee85b 100644 --- a/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java +++ b/optional/src/org.apache.sis.referencing.database/main/org/apache/sis/resources/embedded/EmbeddedResources.java @@ -17,7 +17,6 @@ package org.apache.sis.resources.embedded; import java.util.Set; -import java.util.Collections; import java.util.Locale; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -63,7 +62,7 @@ public class EmbeddedResources extends InstallationResources { */ @Override public Set<String> getAuthorities() { - return Collections.singleton(MetadataServices.EMBEDDED); + return Set.of(MetadataServices.EMBEDDED); } /**