diff --git a/Base/gradle.properties b/Base/gradle.properties index c186bbfdde1..9e8588f2620 100644 --- a/Base/gradle.properties +++ b/Base/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/Stats/src/main/java/io/deephaven/stats/util/OSUtil.java b/Base/src/main/java/io/deephaven/base/OSUtil.java similarity index 98% rename from Stats/src/main/java/io/deephaven/stats/util/OSUtil.java rename to Base/src/main/java/io/deephaven/base/OSUtil.java index 4eceea9a42b..91167f03dfc 100644 --- a/Stats/src/main/java/io/deephaven/stats/util/OSUtil.java +++ b/Base/src/main/java/io/deephaven/base/OSUtil.java @@ -1,7 +1,7 @@ // // Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending // -package io.deephaven.stats.util; +package io.deephaven.base; import org.jetbrains.annotations.NotNull; diff --git a/Configuration/build.gradle b/Configuration/build.gradle index c0cca8e0f6b..0a24f6759b5 100644 --- a/Configuration/build.gradle +++ b/Configuration/build.gradle @@ -7,7 +7,6 @@ dependencies { api libs.trove implementation project(':Base') - implementation project(':DataStructures') implementation project(':IO') implementation project(':log-factory') implementation libs.commons.lang3 diff --git a/Configuration/gradle.properties b/Configuration/gradle.properties index c186bbfdde1..9e8588f2620 100644 --- a/Configuration/gradle.properties +++ b/Configuration/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/Configuration/src/main/java/io/deephaven/configuration/CacheDir.java b/Configuration/src/main/java/io/deephaven/configuration/CacheDir.java index 40441103898..ad403708959 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/CacheDir.java +++ b/Configuration/src/main/java/io/deephaven/configuration/CacheDir.java @@ -4,6 +4,7 @@ package io.deephaven.configuration; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; /** @@ -30,9 +31,8 @@ public final class CacheDir { * @return the cache dir */ public static Path get() { - return viaProperty() - .or(CacheDir::viaEnvVar) - .map(Path::of) + return getOptional() + .map(Paths::get) .orElseGet(CacheDir::viaTmpDir); } @@ -45,12 +45,20 @@ public static Path get() { * @return the cache directory */ public static Path getOrSet(String defaultValue) { - final String existing = viaProperty().or(CacheDir::viaEnvVar).orElse(null); + final String existing = getOptional().orElse(null); if (existing != null) { - return Path.of(existing); + return Paths.get(existing); } System.setProperty(PROPERTY, defaultValue); - return Path.of(defaultValue); + return Paths.get(defaultValue); + } + + private static Optional getOptional() { + Optional optional = viaProperty(); + if (!optional.isPresent()) { + optional = viaEnvVar(); + } + return optional; } private static Optional viaProperty() { @@ -62,6 +70,6 @@ private static Optional viaEnvVar() { } private static Path viaTmpDir() { - return Path.of(System.getProperty(JAVA_IO_TMPDIR), "deephaven", "cache"); + return Paths.get(System.getProperty(JAVA_IO_TMPDIR), "deephaven", "cache"); } } diff --git a/Configuration/src/main/java/io/deephaven/configuration/ConfigDir.java b/Configuration/src/main/java/io/deephaven/configuration/ConfigDir.java index bd1f5afb083..8c1a2aca2b4 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/ConfigDir.java +++ b/Configuration/src/main/java/io/deephaven/configuration/ConfigDir.java @@ -5,6 +5,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; public final class ConfigDir { @@ -21,9 +22,7 @@ public final class ConfigDir { * @return the config directory */ public static Optional get() { - return viaProperty() - .or(ConfigDir::viaEnvVar) - .map(Path::of); + return getOptional().map(Paths::get); } /** @@ -35,14 +34,12 @@ public static Optional get() { * @return the config directory */ public static Path getOrSet(String defaultValue) { - final String existing = viaProperty() - .or(ConfigDir::viaEnvVar) - .orElse(null); + final String existing = getOptional().orElse(null); if (existing != null) { - return Path.of(existing); + return Paths.get(existing); } System.setProperty(PROPERTY, defaultValue); - return Path.of(defaultValue); + return Paths.get(defaultValue); } /** @@ -53,10 +50,11 @@ public static Path getOrSet(String defaultValue) { * @return the configuration file */ public static String configurationFile() { - return Optional - .ofNullable(System.getProperty(ROOT_FILE_PROP)) - .or(ConfigDir::configDirectoryFileIfExists) - .orElse(DEFAULT_CONFIGURATION_FILE); + Optional optional = Optional.ofNullable(System.getProperty(ROOT_FILE_PROP)); + if (!optional.isPresent()) { + optional = configDirectoryFileIfExists(); + } + return optional.orElse(DEFAULT_CONFIGURATION_FILE); } private static Optional configDirectoryFileIfExists() { @@ -66,6 +64,14 @@ private static Optional configDirectoryFileIfExists() { .map(Path::toString); } + private static Optional getOptional() { + Optional optional = viaProperty(); + if (!optional.isPresent()) { + optional = viaEnvVar(); + } + return optional; + } + private static Path defaultFileName(Path p) { return p.resolve(DEFAULT_CONFIG_FILE_NAME); } diff --git a/Configuration/src/main/java/io/deephaven/configuration/Configuration.java b/Configuration/src/main/java/io/deephaven/configuration/Configuration.java index 49933a696a5..ffc6f4d541b 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/Configuration.java +++ b/Configuration/src/main/java/io/deephaven/configuration/Configuration.java @@ -4,54 +4,186 @@ package io.deephaven.configuration; import io.deephaven.internal.log.Bootstrap; -import io.deephaven.io.logger.Logger; import io.deephaven.internal.log.LoggerFactory; +import io.deephaven.io.logger.Logger; import org.jetbrains.annotations.NotNull; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; import java.time.ZoneId; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; /** * Utility class to provide an enhanced view and common access point for java properties files, as well as common * configuration pieces. */ @SuppressWarnings({"WeakerAccess", "unused"}) -public class Configuration extends PropertyFile { - +public abstract class Configuration extends PropertyFile { public static final String QUIET_PROPERTY = "configuration.quiet"; - private static NullableConfiguration INSTANCE = null; - private static final Logger log = LoggerFactory.getLogger(Configuration.class); + private static Configuration DEFAULT; + private static final Map NAMED_CONFIGURATIONS = new ConcurrentHashMap<>(); + private final Supplier contextSupplier; + // This should never be null to meet the contract for getContextKeyValues() private Collection contextKeys = Collections.emptySet(); + /** + * The default Configuration implementation, which loads properties from the default property file in addition to + * System properties. + */ + private static class Default extends Configuration { + Default(@NotNull final Supplier contextSupplier) { + super(contextSupplier); + init(); + } + + @Override + protected String determinePropertyFile() { + return ConfigDir.configurationFile(); + } + + @SuppressWarnings("unused") + public String getPropertyNullable(String propertyName) { + return properties.getProperty(propertyName); + } + } + + /** + * A Named configuration that loads values from the file defined by the property `Configuration.name.rootFile`. + */ + private static class Named extends Configuration { + private final String name; + + Named(@NotNull final String name, @NotNull final Supplier contextSupplier) { + super(contextSupplier); + this.name = name; + init(); + } + + @Override + protected String determinePropertyFile() { + final String propFile = System.getProperty("Configuration." + name + ".rootFile"); + if (propFile == null) { + throw new ConfigurationException("No property file defined for named configuration " + name); + } + + return propFile; + } + } + /** * Get the default Configuration instance. * * @return the single instance of Configuration allowed in an application */ public static Configuration getInstance() { - if (INSTANCE == null) { - INSTANCE = new NullableConfiguration(); + if (DEFAULT == null) { + DEFAULT = new Default(DefaultConfigurationContext::new); } - return INSTANCE; + return DEFAULT; + } + + /** + * Get the {@link Configuration} for the specified name. + * + * @param name the name of the configuration to load + * @return the named configuration. + * @throws ConfigurationException if the named configuration could not be loaded. + */ + public static Configuration getNamed(@NotNull final String name) throws ConfigurationException { + return NAMED_CONFIGURATIONS.computeIfAbsent(name, (k) -> { + try { + return new Named(name, DefaultConfigurationContext::new); + } catch (ConfigurationException ex) { + throw new ConfigurationException("Unable to load named configuration " + name, ex); + } + }); + } + + /** + * Clear all currently loaded Configurations so that they may be loaded anew. + */ + public static void reset() { + DEFAULT = null; + NAMED_CONFIGURATIONS.clear(); + } + + /** + * Clear the specified named Configuration so it may be loaded anew. + * + * @param name the Configuration to clear. + */ + public static void reset(@NotNull final String name) { + NAMED_CONFIGURATIONS.remove(name); + } + + /** + * Create a new, non-cached, default Configuration instance. + * + * @return a new Configuration instance, guaranteed to not be cached. + */ + public static Configuration newStandaloneConfiguration() { + return newStandaloneConfiguration(DefaultConfigurationContext::new); + } + + /** + * Create a new, non-cached, default Configuration instance. + * + * @param contextSupplier the supplier for {@link ConfigurationContext} + * @return a new Configuration instance, guaranteed to not be cached. + */ + public static Configuration newStandaloneConfiguration( + @NotNull final Supplier contextSupplier) { + return new Default(contextSupplier); + } + + /** + * Create a new, non-cached, named Configuration instance. + * + * @param name the configuration name + * @return a new Configuration instance, guaranteed to not be cached. + */ + public static Configuration newStandaloneConfiguration(@NotNull final String name) { + return newStandaloneConfiguration(name, DefaultConfigurationContext::new); } + /** + * Create a new, non-cached, named Configuration instance. + * + * @param name the configuration name + * @param contextSupplier the supplier for {@link ConfigurationContext} + * @return a new Configuration instance, guaranteed to not be cached. + */ @SuppressWarnings("UnusedReturnValue") - public static Configuration newConfigurationForTesting() { - return new Configuration(); + public static Configuration newStandaloneConfiguration( + @NotNull final String name, + @NotNull final Supplier contextSupplier) { + return new Named(name, contextSupplier); + } + + protected Configuration(@NotNull final Supplier contextSupplier) { + this.contextSupplier = contextSupplier; } - protected Configuration() { + /** + * Initialize the configuration. This will be sensitive to any system properties that configure the property file + * path. + */ + protected void init() { final String configurationFile; try { configurationFile = reloadProperties(); } catch (IOException x) { throw new ConfigurationException("Could not process configuration from file", x); } + // The quiet property is available because things like shell scripts may be parsing our System.out and they // don't want to have to deal with these log messages if (!isQuiet()) { @@ -68,7 +200,7 @@ protected Configuration() { * @throws ConfigurationException if the property stream cannot be opened */ private void load(String fileName, boolean ignoreScope) throws IOException, ConfigurationException { - final ParsedProperties temp = new ParsedProperties(ignoreScope); + final ParsedProperties temp = new ParsedProperties(ignoreScope, contextSupplier.get()); // we explicitly want to set 'properties' here so that if we get an error while loading, anything before that // error shows up. // That is very helpful in debugging. @@ -175,6 +307,8 @@ public String reloadProperties() throws IOException, ConfigurationException { return reloadProperties(false); } + protected abstract String determinePropertyFile(); + /** * Reload properties, optionally ignoring scope sections - used for testing * @@ -184,7 +318,7 @@ public String reloadProperties() throws IOException, ConfigurationException { * @throws ConfigurationException if the property stream cannot be opened */ String reloadProperties(boolean ignoreScope) throws IOException, ConfigurationException { - final String propertyFile = ConfigDir.configurationFile(); + final String propertyFile = determinePropertyFile(); load(propertyFile, ignoreScope); // If any system properties exist with the same name as a property that's been declared final, that will // generate an exception the same way it would inside the properties file. @@ -192,23 +326,6 @@ String reloadProperties(boolean ignoreScope) throws IOException, ConfigurationEx return propertyFile; } - /** - * ONLY the service factory is allowed to get null properties and ONLY for the purposes of using default profiles - * when one doesn't exist. This has been relocated here after many people are using defaults/nulls in the code when - * it's not allowed. - */ - @SuppressWarnings("WeakerAccess") - public static class NullableConfiguration extends Configuration { - NullableConfiguration() { - super(); - } - - @SuppressWarnings("unused") - public String getPropertyNullable(String propertyName) { - return properties.getProperty(propertyName); - } - } - // only used by main() method below, normally configs are loaded from the classpath private Properties load(String path, String propFileName) throws IOException { final Properties temp = new ParsedProperties(); @@ -216,7 +333,6 @@ private Properties load(String path, String propFileName) throws IOException { return temp; } - /** * The following main method compares two directories of prop files and outputs a CSV report of the differences. * Usually run before the release of a new version into prod @@ -257,7 +373,7 @@ public static void main(String[] args) { private static String propFileDiffReport(Set includedProperties, String dir1, String file1, String dir2, String file2, String dir3, String file3, boolean useDiffKeys) throws IOException { StringBuilder out = new StringBuilder(); - Configuration configuration = new Configuration(); + Configuration configuration = new Default(DefaultConfigurationContext::new); Properties leftProperties = configuration.load(dir1, file1); Properties rightProperties = configuration.load(dir2, file2); Properties right2Properties; diff --git a/Configuration/src/main/java/io/deephaven/configuration/ConfigurationContext.java b/Configuration/src/main/java/io/deephaven/configuration/ConfigurationContext.java index e9becf5f777..73d27453da5 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/ConfigurationContext.java +++ b/Configuration/src/main/java/io/deephaven/configuration/ConfigurationContext.java @@ -3,132 +3,27 @@ // package io.deephaven.configuration; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.*; +import java.util.Collection; +import java.util.List; /** - * A class for determining the environment that a configuration is running within. + * An interface for classes that determine the environment a {@link Configuration} is running within. */ -class ConfigurationContext { - // Note that we explicitly use 'host' as a value in the configuration files. - // If a system property named 'host' exists, it will be ignored. - private static final String HOSTNAME = "host"; - // Note that we explicitly allow the use of 'process' as a shorthand for 'process.name' in the configuration files. - // If a system property named 'process' exists, it will be ignored in favor of 'process.name'. - static final String PROCESS_NAME_PROPERTY = "process"; - - final private Map contextItems; - final private Collection hostOptions; - - /** - * Create a ConfigurationContext and preload the non-system tokens. - */ - public ConfigurationContext() { - contextItems = new HashMap<>(); - hostOptions = new ArrayList<>(); - contextItems.put(PROCESS_NAME_PROPERTY, getSystemProperty("process.name")); - } - +public interface ConfigurationContext { /** * Check whether the current system context matches one of the requested values in the specified scope - * + * * @param token The name of the property to check * @param targetValues A list of possible values for the specified property, such as 'process.name=foo' * @return True if the specified property currently has a value equal to one of the target values, false otherwise. */ - public boolean matches(final String token, final List targetValues) { - // Mostly this is just checking system properties, but we also have one special case; the hostname is allowed to - // be several different potential values (short name, FQDN, etc), so we have to check that separately. - if (token.toLowerCase().equals(HOSTNAME)) { - if (hostOptions.isEmpty()) { - populateHostnames(); - } - for (String aHostName : hostOptions) { - if (targetValues.contains(aHostName.toLowerCase())) - return true; - } - return false; - } else { - // check that the token value in the context matches for the current scope item - final String contextValue = getContextItem(token); - if (contextValue == null) - return false; - return targetValues.contains(contextValue); - } - } - - /** - * Retrieve a specified context item. These are usually but not necessarily system properties or a small number of - * other environmental factors. - * - * @param token The name of the context item to look up. - * @return The current value of the specified context item, or null if no value exists. - */ - private String getContextItem(final String token) { - if (contextItems.containsKey(token)) { - return contextItems.get(token); - } - // If we don't have an existing context item by this name, see if we have a matching system property - return getSystemProperty(token); - } - - /** - * Get the hostname of the current system where this is running, along with the IP address and fully-qualified name. - */ - private void populateHostnames() { - try { - final InetAddress address = InetAddress.getLocalHost(); - // allow the machine to be identified by its hostname - final String hostName = address.getHostName(); - hostOptions.add(hostName); - - // Allow the machine to be identified by its IP address - final String numericalAddress = address.getHostAddress(); - hostOptions.add(numericalAddress); - - // Allow the machine to be specified by the fully-qualified domain name - if available. - // If we can't get the FQDN, this just returns the IP address, so don't repeat that. - final String fqdn = address.getCanonicalHostName(); - if (!fqdn.equals(numericalAddress)) { - hostOptions.add(address.getCanonicalHostName()); - } - // Also allow the machine to be specified by the simple machine name, if one exists. - final int dotPosition = hostName.indexOf("."); - // If the name is X.y, then also allow just the leading 'X' - if (dotPosition > 0) { - // noinspection RedundantStringOperation - hostOptions.add(address.getHostName().substring(0, dotPosition)); - } - - } catch (UnknownHostException e) { - // If somehow we can't get the current host name, then don't apply any host-specific parameters. - System.err.println("Unable to get current host name. Host-specific configuration items will be ignored."); - } - } - - /** - * Retrieve and store a specified system property's value. - * - * @param propertyName The system property to look up. If a value exists, it will be cached for later retrieval. - * @return The value of the requested system property, or null if the property has no set value. - */ - private String getSystemProperty(final String propertyName) { - final String propVal = System.getProperty(propertyName); - if (propVal != null) { - contextItems.put(propertyName, propVal); - } - return propVal; - } + boolean matches(String token, List targetValues); /** - * Return the configuration contexts. This is the list of system properties that may have been used to parse the - * configuration file. This collection will be immutable. + * Return the configuration contexts. This is the list of properties that may have been used to parse the + * configuration file. Implementations must be sure to return an immutable collection. * * @return the configuration contexts. */ - Collection getContextKeyValues() { - // Create a new HashSet, so that changes to the underlying contextItems don't find their way back to the caller - return Collections.unmodifiableCollection(new HashSet<>(contextItems.keySet())); - } + Collection getContextKeyValues(); } diff --git a/Configuration/src/main/java/io/deephaven/configuration/DataDir.java b/Configuration/src/main/java/io/deephaven/configuration/DataDir.java index d2866ae8162..5a5bdfaacbb 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/DataDir.java +++ b/Configuration/src/main/java/io/deephaven/configuration/DataDir.java @@ -4,6 +4,7 @@ package io.deephaven.configuration; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; public class DataDir { @@ -22,10 +23,7 @@ public class DataDir { * @return the data directory */ public static Path get() { - return Path.of(viaProperty() - .or(DataDir::viaWorkspace) - .or(DataDir::viaEnvironmentVariable) - .orElse(DEFAULT_DATA_DIR)); + return Paths.get(getOptional().orElse(DEFAULT_DATA_DIR)); } /** @@ -37,15 +35,23 @@ public static Path get() { * @return the data directory */ public static Path getOrSet(String defaultValue) { - final String existing = viaProperty() - .or(DataDir::viaWorkspace) - .or(DataDir::viaEnvironmentVariable) - .orElse(null); + final String existing = getOptional().orElse(null); if (existing != null) { - return Path.of(existing); + return Paths.get(existing); } System.setProperty(PROPERTY, defaultValue); - return Path.of(defaultValue); + return Paths.get(defaultValue); + } + + private static Optional getOptional() { + Optional optional = viaProperty(); + if (!optional.isPresent()) { + optional = viaWorkspace(); + } + if (!optional.isPresent()) { + optional = viaEnvironmentVariable(); + } + return optional; } private static Optional viaProperty() { diff --git a/Configuration/src/main/java/io/deephaven/configuration/DefaultConfigurationContext.java b/Configuration/src/main/java/io/deephaven/configuration/DefaultConfigurationContext.java new file mode 100644 index 00000000000..64049ab4858 --- /dev/null +++ b/Configuration/src/main/java/io/deephaven/configuration/DefaultConfigurationContext.java @@ -0,0 +1,123 @@ +// +// Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending +// +package io.deephaven.configuration; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; + +/** + * Default {@link ConfigurationContext} implementation. + */ +public class DefaultConfigurationContext implements ConfigurationContext { + // Note that we explicitly use 'host' as a value in the configuration files. + // If a system property named 'host' exists, it will be ignored. + private static final String HOSTNAME = "host"; + // Note that we explicitly allow the use of 'process' as a shorthand for 'process.name' in the configuration files. + // If a system property named 'process' exists, it will be ignored in favor of 'process.name'. + static final String PROCESS_NAME_PROPERTY = "process"; + + final private Map contextItems; + final private Collection hostOptions; + + /** + * Create a ConfigurationContext and preload the non-system tokens. + */ + public DefaultConfigurationContext() { + contextItems = new HashMap<>(); + hostOptions = new ArrayList<>(); + contextItems.put(PROCESS_NAME_PROPERTY, getSystemProperty("process.name")); + } + + @Override + public boolean matches(final String token, final List targetValues) { + // Mostly this is just checking system properties, but we also have one special case; the hostname is allowed to + // be several different potential values (short name, FQDN, etc), so we have to check that separately. + if (token.toLowerCase().equals(HOSTNAME)) { + if (hostOptions.isEmpty()) { + populateHostnames(); + } + for (String aHostName : hostOptions) { + if (targetValues.contains(aHostName.toLowerCase())) + return true; + } + return false; + } else { + // check that the token value in the context matches for the current scope item + final String contextValue = getContextItem(token); + if (contextValue == null) + return false; + return targetValues.contains(contextValue); + } + } + + /** + * Retrieve a specified context item. These are usually but not necessarily system properties or a small number of + * other environmental factors. + * + * @param token The name of the context item to look up. + * @return The current value of the specified context item, or null if no value exists. + */ + private String getContextItem(final String token) { + if (contextItems.containsKey(token)) { + return contextItems.get(token); + } + // If we don't have an existing context item by this name, see if we have a matching system property + return getSystemProperty(token); + } + + /** + * Get the hostname of the current system where this is running, along with the IP address and fully-qualified name. + */ + private void populateHostnames() { + try { + final InetAddress address = InetAddress.getLocalHost(); + // allow the machine to be identified by its hostname + final String hostName = address.getHostName(); + hostOptions.add(hostName); + + // Allow the machine to be identified by its IP address + final String numericalAddress = address.getHostAddress(); + hostOptions.add(numericalAddress); + + // Allow the machine to be specified by the fully-qualified domain name - if available. + // If we can't get the FQDN, this just returns the IP address, so don't repeat that. + final String fqdn = address.getCanonicalHostName(); + if (!fqdn.equals(numericalAddress)) { + hostOptions.add(address.getCanonicalHostName()); + } + // Also allow the machine to be specified by the simple machine name, if one exists. + final int dotPosition = hostName.indexOf("."); + // If the name is X.y, then also allow just the leading 'X' + if (dotPosition > 0) { + // noinspection RedundantStringOperation + hostOptions.add(address.getHostName().substring(0, dotPosition)); + } + + } catch (UnknownHostException e) { + // If somehow we can't get the current host name, then don't apply any host-specific parameters. + System.err.println("Unable to get current host name. Host-specific configuration items will be ignored."); + } + } + + /** + * Retrieve and store a specified system property's value. + * + * @param propertyName The system property to look up. If a value exists, it will be cached for later retrieval. + * @return The value of the requested system property, or null if the property has no set value. + */ + private String getSystemProperty(final String propertyName) { + final String propVal = System.getProperty(propertyName); + if (propVal != null) { + contextItems.put(propertyName, propVal); + } + return propVal; + } + + @Override + public Collection getContextKeyValues() { + // Create a new HashSet, so that changes to the underlying contextItems don't find their way back to the caller + return Collections.unmodifiableCollection(new HashSet<>(contextItems.keySet())); + } +} diff --git a/Configuration/src/main/java/io/deephaven/configuration/ParsedProperties.java b/Configuration/src/main/java/io/deephaven/configuration/ParsedProperties.java index 888d082b451..bb05427883a 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/ParsedProperties.java +++ b/Configuration/src/main/java/io/deephaven/configuration/ParsedProperties.java @@ -3,17 +3,17 @@ // package io.deephaven.configuration; +import io.deephaven.internal.log.LoggerFactory; +import io.deephaven.io.logger.Logger; +import org.apache.commons.lang3.StringEscapeUtils; +import org.jetbrains.annotations.NotNull; + import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import io.deephaven.io.logger.Logger; -import io.deephaven.internal.log.LoggerFactory; -import org.apache.commons.lang3.StringEscapeUtils; -import org.jetbrains.annotations.NotNull; - /** * Class for reading in a customized properties file, applying only the locally-relevant properties and keeping track of * which properties may not be further modified. Maintains the ordering of the properties from the input file. @@ -105,7 +105,18 @@ public ParsedProperties() { * PropertyInspector when checking whether required or disallowed properties are present. */ public ParsedProperties(final boolean ignoreScopes) { - context = new ConfigurationContext(); + this(ignoreScopes, new DefaultConfigurationContext()); + } + + /** + * A constructor that starts with no existing scoped or final properties. + * + * @param ignoreScopes True if this parser should ignore scope restrictions, false otherwise. Used by the + * PropertyInspector when checking whether required or disallowed properties are present. + * @param context the {@link ConfigurationContext} for determining environment settings + */ + public ParsedProperties(final boolean ignoreScopes, @NotNull final ConfigurationContext context) { + this.context = context; finalProperties = new HashSet<>(); lineNumbers = new HashMap<>(); props = new LinkedHashMap<>(); diff --git a/Configuration/src/test/java/io/deephaven/configuration/TestConfiguration.java b/Configuration/src/test/java/io/deephaven/configuration/TestConfiguration.java index 7f3069efcf6..472956e726e 100644 --- a/Configuration/src/test/java/io/deephaven/configuration/TestConfiguration.java +++ b/Configuration/src/test/java/io/deephaven/configuration/TestConfiguration.java @@ -105,7 +105,7 @@ public void testProperties() { try { System.setProperty(FILENAME_PROPERTY, "nonexistent"); - Configuration.newConfigurationForTesting(); + Configuration.newStandaloneConfiguration(); fail("Expected exception"); } catch (ConfigurationException expected) { // Expected @@ -192,7 +192,7 @@ public void testReservedKeywordAsDeclaration() { * an on-the-fly property */ public void testContext() throws Exception { - final String oldProcessName = System.getProperty(ConfigurationContext.PROCESS_NAME_PROPERTY); + final String oldProcessName = System.getProperty(DefaultConfigurationContext.PROCESS_NAME_PROPERTY); final String testProp = "testproperty"; try { System.setProperty(FILENAME_PROPERTY, "resources/test-context.prop"); @@ -255,7 +255,7 @@ public void testContext() throws Exception { } public void testContextIgnoreScope() throws Exception { - final String oldProcessName = System.getProperty(ConfigurationContext.PROCESS_NAME_PROPERTY); + final String oldProcessName = System.getProperty(DefaultConfigurationContext.PROCESS_NAME_PROPERTY); final String testProp = "testproperty"; try { System.setProperty(FILENAME_PROPERTY, "resources/test-context.prop"); @@ -324,7 +324,7 @@ public void testIncludeFinal() { */ private void runTestsOnFinalKeyword(final String filename, final String contextName, final String beforeValue, final String finalTestValue, final String includeValue) { - final String oldProcessName = System.getProperty(ConfigurationContext.PROCESS_NAME_PROPERTY); + final String oldProcessName = System.getProperty(DefaultConfigurationContext.PROCESS_NAME_PROPERTY); final String testPropContextIdentifier = "testbatch"; final String beforeTestProperty = "beforetest"; final String finalTestProperty = "finaltest"; @@ -358,7 +358,7 @@ private void runTestsOnFinalKeyword(final String filename, final String contextN } finally { if (oldProcessName != null) - System.setProperty(ConfigurationContext.PROCESS_NAME_PROPERTY, oldProcessName); + System.setProperty(DefaultConfigurationContext.PROCESS_NAME_PROPERTY, oldProcessName); System.clearProperty(testPropContextIdentifier); } } @@ -469,5 +469,95 @@ public void testShowHistory() { System.out.println("-------------- End show history -----------------"); } - + public void testNamedConfiguration() { + System.setProperty(FILENAME_PROPERTY, "resources/lib-tests.prop"); + System.setProperty("Configuration.Test1.rootFile", "resources/test1.prop"); + System.setProperty("Configuration.Test2.rootFile", "resources/test2.prop"); + System.setProperty("Configuration.Test3.rootFile", "resources/test3.prop"); + + Configuration dc = Configuration.getInstance(); + Configuration c1 = Configuration.getNamed("Test1"); + Configuration c2 = Configuration.getNamed("Test2"); + Configuration c3 = Configuration.getNamed("Test3"); + + assertEquals("cache", dc.getProperty("cacheDir")); + assertNull(c1.getStringWithDefault("cacheDir", null)); + assertNull(c2.getStringWithDefault("cacheDir", null)); + assertNull(c3.getStringWithDefault("cacheDir", null)); + + assertNull(dc.getStringWithDefault("test1", null)); + assertEquals("true", c1.getProperty("test1")); + assertNull(c2.getStringWithDefault("test1", null)); + assertNull(c3.getStringWithDefault("test1", null)); + + assertNull(dc.getStringWithDefault("test2", null)); + assertNull(c1.getStringWithDefault("test2", null)); + assertEquals("true", c2.getProperty("test2")); + assertNull(c3.getStringWithDefault("test2", null)); + + assertNull(dc.getStringWithDefault("test3", null)); + // test1 imports test3, so expected. + assertEquals("true", c1.getProperty("test3")); + assertNull(c2.getStringWithDefault("test3", null)); + assertEquals("true", c3.getProperty("test3")); + + // We'll try a reset of one of them + System.setProperty("Configuration.Test1.rootFile", "resources/test2.prop"); + Configuration.reset("Test1"); + dc = Configuration.getInstance(); + c1 = Configuration.getNamed("Test1"); + c2 = Configuration.getNamed("Test2"); + c3 = Configuration.getNamed("Test3"); + + assertEquals("cache", dc.getProperty("cacheDir")); + assertNull(c1.getStringWithDefault("cacheDir", null)); + assertNull(c2.getStringWithDefault("cacheDir", null)); + assertNull(c3.getStringWithDefault("cacheDir", null)); + + assertNull(dc.getStringWithDefault("test1", null)); + assertNull(c1.getStringWithDefault("test1", null)); + assertNull(c2.getStringWithDefault("test1", null)); + assertNull(c3.getStringWithDefault("test1", null)); + + assertNull(dc.getStringWithDefault("test2", null)); + assertEquals("true", c1.getProperty("test2")); + assertEquals("true", c2.getProperty("test2")); + assertNull(c3.getStringWithDefault("test2", null)); + + assertNull(dc.getStringWithDefault("test3", null)); + assertNull(c1.getStringWithDefault("test3", null)); + assertNull(c2.getStringWithDefault("test3", null)); + assertEquals("true", c3.getProperty("test3")); + + // Reset all of them + System.setProperty(FILENAME_PROPERTY, "resources/test-multiline.prop"); + System.setProperty("Configuration.Test1.rootFile", "resources/lib-tests.prop"); + System.setProperty("Configuration.Test2.rootFile", "resources/test3.prop"); + System.setProperty("Configuration.Test3.rootFile", "resources/test2.prop"); + Configuration.reset(); + dc = Configuration.getInstance(); + c1 = Configuration.getNamed("Test1"); + c2 = Configuration.getNamed("Test2"); + c3 = Configuration.getNamed("Test3"); + + assertEquals("abcdefghi", dc.getProperty("foo")); + assertNull(c1.getStringWithDefault("foo", null)); + assertNull(c2.getStringWithDefault("foo", null)); + assertNull(c3.getStringWithDefault("foo", null)); + + assertNull(dc.getStringWithDefault("cache", null)); + assertEquals("cache", c1.getProperty("cacheDir")); + assertNull(c2.getStringWithDefault("cache", null)); + assertNull(c3.getStringWithDefault("cache", null)); + + assertNull(dc.getStringWithDefault("test3", null)); + assertNull(c1.getStringWithDefault("test3", null)); + assertEquals("true", c2.getProperty("test3")); + assertNull(c3.getStringWithDefault("test3", null)); + + assertNull(dc.getStringWithDefault("test2", null)); + assertNull(c1.getStringWithDefault("test2", null)); + assertNull(c2.getStringWithDefault("test2", null)); + assertEquals("true", c3.getProperty("test2")); + } } diff --git a/IO/gradle.properties b/IO/gradle.properties index c186bbfdde1..9e8588f2620 100644 --- a/IO/gradle.properties +++ b/IO/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/IO/src/main/java/io/deephaven/io/log/LogEntry.java b/IO/src/main/java/io/deephaven/io/log/LogEntry.java index 124dadd77ff..78ce12051e3 100644 --- a/IO/src/main/java/io/deephaven/io/log/LogEntry.java +++ b/IO/src/main/java/io/deephaven/io/log/LogEntry.java @@ -8,6 +8,7 @@ import io.deephaven.base.log.LogOutputAppendable; import io.deephaven.base.text.TimestampBuffer; import io.deephaven.base.text.TimestampBufferMicros; +import org.jetbrains.annotations.ApiStatus; import java.nio.ByteBuffer; @@ -105,7 +106,7 @@ default LogEntry appendDouble(Double f) { * @return the resulting {@code LogEntry} */ default LogEntry appendDouble(final double doubleValue, final int decimalPlaces) { - return appendDoubleDecimalPlacesImpl(this, doubleValue, decimalPlaces, decimalPlaces); + return Helper.appendDoubleDecimalPlacesImpl(this, doubleValue, decimalPlaces, decimalPlaces); } /** @@ -121,82 +122,86 @@ default LogEntry appendDouble(final double doubleValue, final int decimalPlaces) */ default LogEntry appendDouble(final double doubleValue, final int decimalPlaces, final int maxTrailingZeroesToDiscard) { - return appendDoubleDecimalPlacesImpl(this, doubleValue, decimalPlaces, + return Helper.appendDoubleDecimalPlacesImpl(this, doubleValue, decimalPlaces, decimalPlaces - maxTrailingZeroesToDiscard); } - private static LogEntry appendDoubleDecimalPlacesImpl( - LogEntry entry, final double doubleValue, final int roundToDecimalPlaces, final int minDecimalPlaces) { - if (roundToDecimalPlaces < 0 || roundToDecimalPlaces > 9) { - throw new IllegalArgumentException("Invalid value for decimalPlaces = " + roundToDecimalPlaces); - } - final int roundToAsPowerOf10 = MathUtil.pow10(roundToDecimalPlaces); - final boolean negative = doubleValue < 0.0; - final long longWeightedValue; - if (negative) { - longWeightedValue = -(long) (-0.5 + doubleValue * roundToAsPowerOf10); - } else { - longWeightedValue = (long) (0.5 + doubleValue * roundToAsPowerOf10); - } - final long integral = longWeightedValue / roundToAsPowerOf10; - if (negative) { - entry = entry.append(-integral); - } else { - entry = entry.append(integral); - } - if (roundToDecimalPlaces == 0) { - return entry; - } - int fractional = (int) (longWeightedValue % roundToAsPowerOf10); - int actualDecimalPlaces = roundToDecimalPlaces; - while (minDecimalPlaces < actualDecimalPlaces && fractional > 0 && (fractional % 10 == 0)) { - fractional /= 10; - --actualDecimalPlaces; - } - if (minDecimalPlaces == 0 && fractional == 0) { - return entry; - } - entry = entry.append("."); - final int base10FractionalDigits = MathUtil.base10digits(fractional); - final int leadingZeroes = actualDecimalPlaces - base10FractionalDigits; - switch (leadingZeroes) { - case 9: - entry = entry.append("000000000"); - case 8: - entry = entry.append("00000000"); - break; - case 7: - entry = entry.append("0000000"); - break; - case 6: - entry = entry.append("000000"); - break; - case 5: - entry = entry.append("00000"); - break; - case 4: - entry = entry.append("0000"); - break; - case 3: - entry = entry.append("000"); - break; - case 2: - entry = entry.append("00"); - break; - case 1: - entry = entry.append("0"); - break; - } - if (fractional == 0) { - return entry; - } - return entry.append(fractional); - } - LogEntry nf(); LogEntry nl(); + @ApiStatus.Internal + final class Helper { + private Helper() {} + + private static LogEntry appendDoubleDecimalPlacesImpl( + LogEntry entry, final double doubleValue, final int roundToDecimalPlaces, final int minDecimalPlaces) { + if (roundToDecimalPlaces < 0 || roundToDecimalPlaces > 9) { + throw new IllegalArgumentException("Invalid value for decimalPlaces = " + roundToDecimalPlaces); + } + final int roundToAsPowerOf10 = MathUtil.pow10(roundToDecimalPlaces); + final boolean negative = doubleValue < 0.0; + final long longWeightedValue; + if (negative) { + longWeightedValue = -(long) (-0.5 + doubleValue * roundToAsPowerOf10); + } else { + longWeightedValue = (long) (0.5 + doubleValue * roundToAsPowerOf10); + } + final long integral = longWeightedValue / roundToAsPowerOf10; + if (negative) { + entry = entry.append(-integral); + } else { + entry = entry.append(integral); + } + if (roundToDecimalPlaces == 0) { + return entry; + } + int fractional = (int) (longWeightedValue % roundToAsPowerOf10); + int actualDecimalPlaces = roundToDecimalPlaces; + while (minDecimalPlaces < actualDecimalPlaces && fractional > 0 && (fractional % 10 == 0)) { + fractional /= 10; + --actualDecimalPlaces; + } + if (minDecimalPlaces == 0 && fractional == 0) { + return entry; + } + entry = entry.append("."); + final int base10FractionalDigits = MathUtil.base10digits(fractional); + final int leadingZeroes = actualDecimalPlaces - base10FractionalDigits; + switch (leadingZeroes) { + case 9: + entry = entry.append("000000000"); + case 8: + entry = entry.append("00000000"); + break; + case 7: + entry = entry.append("0000000"); + break; + case 6: + entry = entry.append("000000"); + break; + case 5: + entry = entry.append("00000"); + break; + case 4: + entry = entry.append("0000"); + break; + case 3: + entry = entry.append("000"); + break; + case 2: + entry = entry.append("00"); + break; + case 1: + entry = entry.append("0"); + break; + } + if (fractional == 0) { + return entry; + } + return entry.append(fractional); + } + } // --------------------------------------------------------------------------------------------- // null implementation diff --git a/IO/src/main/java/io/deephaven/io/streams/ByteBufferOutputStream.java b/IO/src/main/java/io/deephaven/io/streams/ByteBufferOutputStream.java index 8a901f26718..1cbfcca9ec2 100644 --- a/IO/src/main/java/io/deephaven/io/streams/ByteBufferOutputStream.java +++ b/IO/src/main/java/io/deephaven/io/streams/ByteBufferOutputStream.java @@ -252,7 +252,7 @@ public ByteBufferOutputStream appendByteBuffer(ByteBuffer bb) throws IOException final int origLimit = bb.limit(); int remaining; while ((remaining = buf.remaining()) < bb.remaining()) { - buf.put(bb.limit(bb.position() + remaining)); + buf.put((ByteBuffer) bb.limit(bb.position() + remaining)); bb.limit(origLimit); updateBuffer(buf.capacity()); } diff --git a/Integrations/build.gradle b/Integrations/build.gradle index ac16784d585..3f03828b672 100644 --- a/Integrations/build.gradle +++ b/Integrations/build.gradle @@ -15,6 +15,7 @@ dependencies { implementation project(':plugin') implementation project(':Configuration') implementation project(':log-factory') + implementation project(":util-thread") implementation libs.commons.lang3 testImplementation project(':engine-test-utils') diff --git a/SevenZip/gradle.properties b/SevenZip/gradle.properties index 1c0cc01b600..ecbff9a0779 100644 --- a/SevenZip/gradle.properties +++ b/SevenZip/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_EXTERNAL +languageLevel=8 diff --git a/Stats/build.gradle b/Stats/build.gradle index dcd55f60a2c..939380a7fa1 100644 --- a/Stats/build.gradle +++ b/Stats/build.gradle @@ -9,6 +9,7 @@ dependencies { implementation project(':Configuration') implementation project(':log-factory') implementation project(':engine-context') + implementation project(':util-thread') compileOnly libs.google.java.allocation.instrumenter testImplementation project(path: ':Base', configuration: 'tests') diff --git a/Stats/src/main/java/io/deephaven/stats/StatsCPUCollector.java b/Stats/src/main/java/io/deephaven/stats/StatsCPUCollector.java index 2ca37c16cef..b4f3a05a259 100644 --- a/Stats/src/main/java/io/deephaven/stats/StatsCPUCollector.java +++ b/Stats/src/main/java/io/deephaven/stats/StatsCPUCollector.java @@ -7,7 +7,7 @@ import io.deephaven.configuration.Configuration; import io.deephaven.internal.log.LoggerFactory; import io.deephaven.io.logger.Logger; -import io.deephaven.stats.util.OSUtil; +import io.deephaven.base.OSUtil; import io.deephaven.base.stats.*; import io.deephaven.hash.KeyedLongObjectHash; import io.deephaven.hash.KeyedLongObjectHashMap; diff --git a/TableLogger/TableLogger.gradle b/TableLogger/TableLogger.gradle index 34421dd4055..b950ec4544a 100644 --- a/TableLogger/TableLogger.gradle +++ b/TableLogger/TableLogger.gradle @@ -5,6 +5,7 @@ plugins { dependencies { implementation project(':Base') implementation project(':Util') + implementation project(":util-pool") testRuntimeOnly project(path: ':configs') testRuntimeOnly project(path: ':test-configs') diff --git a/Util/Util.gradle b/Util/Util.gradle index f25646a72a7..9bdd1deeab5 100644 --- a/Util/Util.gradle +++ b/Util/Util.gradle @@ -10,7 +10,6 @@ dependencies { implementation libs.jdom2 implementation libs.commons.compress - implementation project(':DataStructures') implementation project(':Configuration') testRuntimeOnly project(path: ':configs') diff --git a/Util/immutables/gradle.properties b/Util/immutables/gradle.properties index 4d202c2484f..d7a5121a152 100644 --- a/Util/immutables/gradle.properties +++ b/Util/immutables/gradle.properties @@ -1,2 +1,2 @@ io.deephaven.project.ProjectType=JAVA_LOCAL -languageLevel=8 \ No newline at end of file +languageLevel=8 diff --git a/Util/pool/build.gradle b/Util/pool/build.gradle new file mode 100644 index 00000000000..28fc23d26eb --- /dev/null +++ b/Util/pool/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java-library' + id 'io.deephaven.project.register' +} + +description 'util-pool: Optional extensions of the io.deephaven.base.pool module' + +dependencies { + api project(':Base') + api project(":IO") + + compileOnly libs.jetbrains.annotations + + testImplementation platform(libs.junit.bom) + testImplementation libs.junit.jupiter + testRuntimeOnly libs.junit.jupiter.engine + testRuntimeOnly libs.junit.platform.launcher + testImplementation project(path: ':Base', configuration: 'tests') +} + +test { + useJUnitPlatform() +} diff --git a/Util/pool/gradle.properties b/Util/pool/gradle.properties new file mode 100644 index 00000000000..9e8588f2620 --- /dev/null +++ b/Util/pool/gradle.properties @@ -0,0 +1,2 @@ +io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/Util/src/main/java/io/deephaven/util/pool/PoolEx.java b/Util/pool/src/main/java/io/deephaven/util/pool/PoolEx.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/pool/PoolEx.java rename to Util/pool/src/main/java/io/deephaven/util/pool/PoolEx.java diff --git a/Util/src/main/java/io/deephaven/util/pool/ThreadSafeFixedSizePool.java b/Util/pool/src/main/java/io/deephaven/util/pool/ThreadSafeFixedSizePool.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/pool/ThreadSafeFixedSizePool.java rename to Util/pool/src/main/java/io/deephaven/util/pool/ThreadSafeFixedSizePool.java diff --git a/Util/src/main/java/io/deephaven/util/pool/ThreadSafeLenientFixedSizePool.java b/Util/pool/src/main/java/io/deephaven/util/pool/ThreadSafeLenientFixedSizePool.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/pool/ThreadSafeLenientFixedSizePool.java rename to Util/pool/src/main/java/io/deephaven/util/pool/ThreadSafeLenientFixedSizePool.java diff --git a/Util/src/test/java/io/deephaven/util/pool/TestThreadSafeFixedSizePool.java b/Util/pool/src/test/java/io/deephaven/util/pool/TestThreadSafeFixedSizePool.java similarity index 97% rename from Util/src/test/java/io/deephaven/util/pool/TestThreadSafeFixedSizePool.java rename to Util/pool/src/test/java/io/deephaven/util/pool/TestThreadSafeFixedSizePool.java index e87ccbbf7f6..e5d6fa5e45d 100644 --- a/Util/src/test/java/io/deephaven/util/pool/TestThreadSafeFixedSizePool.java +++ b/Util/pool/src/test/java/io/deephaven/util/pool/TestThreadSafeFixedSizePool.java @@ -7,13 +7,15 @@ import io.deephaven.base.pool.MockClearingProcedure; import io.deephaven.base.pool.Pool; import io.deephaven.base.verify.RequirementFailure; -import junit.framework.TestCase; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; // -------------------------------------------------------------------- /** * Tests for {@link io.deephaven.base.pool.ThreadSafeFixedSizePool}. */ -public class TestThreadSafeFixedSizePool extends TestCase { +public class TestThreadSafeFixedSizePool { private MockFactory m_mockObjectFactory; private MockClearingProcedure m_mockClearingProcedure; @@ -27,6 +29,7 @@ public class TestThreadSafeFixedSizePool extends TestCase { }; // ---------------------------------------------------------------- + @Test public void testThreadSafeFixedSizePool() { m_mockObjectFactory = new MockFactory(); m_mockClearingProcedure = new MockClearingProcedure(); @@ -82,6 +85,7 @@ public void testThreadSafeFixedSizePool() { } // ---------------------------------------------------------------- + @Test public void testThreadSafeFixedSizePoolNoClearingProcedure() { m_mockObjectFactory = new MockFactory(); m_mockClearingProcedure = new MockClearingProcedure(); @@ -112,6 +116,7 @@ public void testThreadSafeFixedSizePoolNoClearingProcedure() { } // ---------------------------------------------------------------- + @Test public void testThreadSafeFixedSizePoolNoFactory() { m_mockObjectFactory = new MockFactory(); m_mockClearingProcedure = new MockClearingProcedure(); diff --git a/Util/src/test/java/io/deephaven/util/pool/TestThreadSafeLenientFixedSizePool.java b/Util/pool/src/test/java/io/deephaven/util/pool/TestThreadSafeLenientFixedSizePool.java similarity index 97% rename from Util/src/test/java/io/deephaven/util/pool/TestThreadSafeLenientFixedSizePool.java rename to Util/pool/src/test/java/io/deephaven/util/pool/TestThreadSafeLenientFixedSizePool.java index 5aae5ea3970..2b645408cf3 100644 --- a/Util/src/test/java/io/deephaven/util/pool/TestThreadSafeLenientFixedSizePool.java +++ b/Util/pool/src/test/java/io/deephaven/util/pool/TestThreadSafeLenientFixedSizePool.java @@ -7,15 +7,17 @@ import io.deephaven.base.pool.MockClearingProcedure; import io.deephaven.base.pool.Pool; import io.deephaven.base.verify.RequirementFailure; -import junit.framework.TestCase; +import org.junit.jupiter.api.Test; import java.util.function.Function; +import static org.junit.jupiter.api.Assertions.*; + // -------------------------------------------------------------------- /** * Tests for {@link io.deephaven.base.pool.ThreadSafeLenientFixedSizePool}. */ -public class TestThreadSafeLenientFixedSizePool extends TestCase { +public class TestThreadSafeLenientFixedSizePool { private MockFactory m_mockObjectFactory; private MockClearingProcedure m_mockClearingProcedure; @@ -29,6 +31,7 @@ public class TestThreadSafeLenientFixedSizePool extends TestCase { }; // ---------------------------------------------------------------- + @Test public void testThreadSafeLenientFixedSizePool() { m_mockObjectFactory = new MockFactory(); m_mockClearingProcedure = new MockClearingProcedure(); @@ -93,6 +96,7 @@ public void testThreadSafeLenientFixedSizePool() { } // ---------------------------------------------------------------- + @Test public void testThreadSafeLenientFixedSizePoolNoClearingProcedure() { m_mockObjectFactory = new MockFactory(); m_mockClearingProcedure = new MockClearingProcedure(); @@ -125,6 +129,7 @@ public void testThreadSafeLenientFixedSizePoolNoClearingProcedure() { } // ---------------------------------------------------------------- + @Test public void testThreadSafeLenientFixedSizePoolNoFactory() { m_mockObjectFactory = new MockFactory(); m_mockClearingProcedure = new MockClearingProcedure(); diff --git a/Util/processenvironment/build.gradle b/Util/processenvironment/build.gradle new file mode 100644 index 00000000000..0911276f3da --- /dev/null +++ b/Util/processenvironment/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java-library' + id 'io.deephaven.project.register' +} + +description 'util-processenvironment: Deephaven ProcessEnvironment and default implementations' + +dependencies { + api project(':Configuration') + api project(':IO') + + implementation project(':util-thread') + implementation project(':log-factory') + implementation project(':Base') + + compileOnly libs.jetbrains.annotations +} diff --git a/Util/processenvironment/gradle.properties b/Util/processenvironment/gradle.properties new file mode 100644 index 00000000000..9e8588f2620 --- /dev/null +++ b/Util/processenvironment/gradle.properties @@ -0,0 +1,2 @@ +io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/Util/src/main/java/io/deephaven/util/process/BaseProcessEnvironment.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/BaseProcessEnvironment.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/BaseProcessEnvironment.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/BaseProcessEnvironment.java diff --git a/Util/src/main/java/io/deephaven/util/process/DefaultFatalErrorReporter.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/DefaultFatalErrorReporter.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/DefaultFatalErrorReporter.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/DefaultFatalErrorReporter.java diff --git a/Util/src/main/java/io/deephaven/util/process/DefaultProcessEnvironment.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/DefaultProcessEnvironment.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/DefaultProcessEnvironment.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/DefaultProcessEnvironment.java diff --git a/Util/src/main/java/io/deephaven/util/process/FatalErrorReporter.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/FatalErrorReporter.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/FatalErrorReporter.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/FatalErrorReporter.java diff --git a/Util/src/main/java/io/deephaven/util/process/FatalErrorReporterBase.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/FatalErrorReporterBase.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/FatalErrorReporterBase.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/FatalErrorReporterBase.java diff --git a/Util/src/main/java/io/deephaven/util/process/LoggerShutdownTask.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/LoggerShutdownTask.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/LoggerShutdownTask.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/LoggerShutdownTask.java diff --git a/Util/src/main/java/io/deephaven/util/process/OnetimeShutdownTask.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/OnetimeShutdownTask.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/OnetimeShutdownTask.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/OnetimeShutdownTask.java diff --git a/Util/src/main/java/io/deephaven/util/process/ProcessEnvironment.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/ProcessEnvironment.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/ProcessEnvironment.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/ProcessEnvironment.java diff --git a/Util/src/main/java/io/deephaven/util/process/ShutdownManager.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/ShutdownManager.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/ShutdownManager.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/ShutdownManager.java diff --git a/Util/src/main/java/io/deephaven/util/process/ShutdownManagerImpl.java b/Util/processenvironment/src/main/java/io/deephaven/util/process/ShutdownManagerImpl.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/process/ShutdownManagerImpl.java rename to Util/processenvironment/src/main/java/io/deephaven/util/process/ShutdownManagerImpl.java diff --git a/Util/thread/build.gradle b/Util/thread/build.gradle new file mode 100644 index 00000000000..ac43e2ca77b --- /dev/null +++ b/Util/thread/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java-library' + id 'io.deephaven.project.register' +} + +description 'util-thread: Deephaven utilities for threading and thread dumps' + +dependencies { + api project(':IO') + + implementation project(':Configuration') + implementation project(':log-factory') + + compileOnly libs.jetbrains.annotations +} diff --git a/Util/thread/gradle.properties b/Util/thread/gradle.properties new file mode 100644 index 00000000000..9e8588f2620 --- /dev/null +++ b/Util/thread/gradle.properties @@ -0,0 +1,2 @@ +io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/Util/src/main/java/io/deephaven/util/thread/NamingThreadFactory.java b/Util/thread/src/main/java/io/deephaven/util/thread/NamingThreadFactory.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/thread/NamingThreadFactory.java rename to Util/thread/src/main/java/io/deephaven/util/thread/NamingThreadFactory.java diff --git a/Util/src/main/java/io/deephaven/util/thread/ThreadDump.java b/Util/thread/src/main/java/io/deephaven/util/thread/ThreadDump.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/thread/ThreadDump.java rename to Util/thread/src/main/java/io/deephaven/util/thread/ThreadDump.java diff --git a/Util/src/main/java/io/deephaven/util/thread/ThreadHelpers.java b/Util/thread/src/main/java/io/deephaven/util/thread/ThreadHelpers.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/thread/ThreadHelpers.java rename to Util/thread/src/main/java/io/deephaven/util/thread/ThreadHelpers.java diff --git a/Util/src/main/java/io/deephaven/util/thread/ThreadInitializationFactory.java b/Util/thread/src/main/java/io/deephaven/util/thread/ThreadInitializationFactory.java similarity index 100% rename from Util/src/main/java/io/deephaven/util/thread/ThreadInitializationFactory.java rename to Util/thread/src/main/java/io/deephaven/util/thread/ThreadInitializationFactory.java diff --git a/clock/gradle.properties b/clock/gradle.properties index c186bbfdde1..9e8588f2620 100644 --- a/clock/gradle.properties +++ b/clock/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/clock/src/main/java/io/deephaven/base/clock/SystemClock.java b/clock/src/main/java/io/deephaven/base/clock/SystemClock.java index da22e6ee38e..27b14cbc522 100644 --- a/clock/src/main/java/io/deephaven/base/clock/SystemClock.java +++ b/clock/src/main/java/io/deephaven/base/clock/SystemClock.java @@ -62,7 +62,7 @@ static SystemClock of() throws ClassNotFoundException, NoSuchMethodException, In case DEFAULT: return serviceLoader().orElse(systemUTC()); case SERVICE_LOADER: - return serviceLoader().orElseThrow(); + return serviceLoader().orElseThrow(() -> new IllegalStateException("Unable to load clock")); case SYSTEM_UTC: return systemUTC(); case SYSTEM_MILLIS: diff --git a/engine/table/build.gradle b/engine/table/build.gradle index 4fdf0595646..171fb1e9731 100644 --- a/engine/table/build.gradle +++ b/engine/table/build.gradle @@ -23,6 +23,8 @@ dependencies { api project(':IO') api project(':codec-api') + implementation project(':util-processenvironment') + implementation project(':util-thread') implementation project(':DHProcess') implementation project(':engine-function') implementation project(':engine-tuple') diff --git a/engine/table/src/test/java/io/deephaven/engine/util/jpy/JpyConfigLoaderTest.java b/engine/table/src/test/java/io/deephaven/engine/util/jpy/JpyConfigLoaderTest.java index 730ecac72bd..294b8518972 100644 --- a/engine/table/src/test/java/io/deephaven/engine/util/jpy/JpyConfigLoaderTest.java +++ b/engine/table/src/test/java/io/deephaven/engine/util/jpy/JpyConfigLoaderTest.java @@ -226,7 +226,7 @@ private static Configuration loadConfig(String configFile) { // and Configuration not an interface... String existingValue = System.getProperty("Configuration.rootFile"); System.setProperty("Configuration.rootFile", configFile); - Configuration config = Configuration.newConfigurationForTesting(); + Configuration config = Configuration.newStandaloneConfiguration(); if (existingValue != null) { System.setProperty("Configuration.rootFile", existingValue); } else { diff --git a/engine/test-utils/build.gradle b/engine/test-utils/build.gradle index 8447aba7f58..2ca92c66019 100644 --- a/engine/test-utils/build.gradle +++ b/engine/test-utils/build.gradle @@ -9,6 +9,8 @@ dependencies { api project(":engine-table") api project(":engine-rowset") + implementation project(":util-processenvironment") + implementation project(":util-thread") implementation project(':Configuration') implementation project(':engine-tuple') api project(':base-test-utils') diff --git a/engine/updategraph/build.gradle b/engine/updategraph/build.gradle index e9bc9e1dd0b..ec8cba4c3b9 100644 --- a/engine/updategraph/build.gradle +++ b/engine/updategraph/build.gradle @@ -10,6 +10,7 @@ dependencies { implementation project(':hotspot') implementation project(':log-factory') implementation project(':Configuration') + implementation project(':util-processenvironment') implementation libs.commons.lang3 compileOnly libs.google.findbugs.jsr305 diff --git a/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/IcebergMinIOTest.java b/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/IcebergMinIOTest.java index d3f5d57edfe..8c2d8aef0f6 100644 --- a/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/IcebergMinIOTest.java +++ b/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/IcebergMinIOTest.java @@ -5,7 +5,7 @@ import io.deephaven.extensions.s3.S3Instructions.Builder; import io.deephaven.extensions.s3.testlib.SingletonContainers.MinIO; -import io.deephaven.stats.util.OSUtil; +import io.deephaven.base.OSUtil; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; diff --git a/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/MinioWarehouseSqliteCatalogTest.java b/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/MinioWarehouseSqliteCatalogTest.java index 16cb97f045d..1a569ab88ab 100644 --- a/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/MinioWarehouseSqliteCatalogTest.java +++ b/extensions/iceberg/s3/src/test/java/io/deephaven/iceberg/util/MinioWarehouseSqliteCatalogTest.java @@ -5,7 +5,7 @@ import io.deephaven.extensions.s3.S3Instructions; import io.deephaven.extensions.s3.testlib.SingletonContainers.MinIO; -import io.deephaven.stats.util.OSUtil; +import io.deephaven.base.OSUtil; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; diff --git a/extensions/parquet/table/src/test/java/io/deephaven/parquet/table/S3ParquetMinIOTest.java b/extensions/parquet/table/src/test/java/io/deephaven/parquet/table/S3ParquetMinIOTest.java index a100f830e5d..c8113421be9 100644 --- a/extensions/parquet/table/src/test/java/io/deephaven/parquet/table/S3ParquetMinIOTest.java +++ b/extensions/parquet/table/src/test/java/io/deephaven/parquet/table/S3ParquetMinIOTest.java @@ -5,7 +5,7 @@ import io.deephaven.extensions.s3.S3Instructions.Builder; import io.deephaven.extensions.s3.testlib.SingletonContainers.MinIO; -import io.deephaven.stats.util.OSUtil; +import io.deephaven.base.OSUtil; import org.junit.Assume; import org.junit.BeforeClass; import software.amazon.awssdk.services.s3.S3AsyncClient; diff --git a/extensions/s3/build.gradle b/extensions/s3/build.gradle index 5f71a8db109..a149d6d24bb 100644 --- a/extensions/s3/build.gradle +++ b/extensions/s3/build.gradle @@ -15,6 +15,7 @@ dependencies { implementation project(':Util') implementation project(':Configuration') implementation project(':log-factory') + implementation project(':util-thread') implementation platform(libs.awssdk.bom) implementation libs.awssdk.s3 diff --git a/extensions/s3/src/test/java/io/deephaven/extensions/s3/S3SeekableChannelSimpleMinIOTest.java b/extensions/s3/src/test/java/io/deephaven/extensions/s3/S3SeekableChannelSimpleMinIOTest.java index 1a91cd0c860..010341d08c2 100644 --- a/extensions/s3/src/test/java/io/deephaven/extensions/s3/S3SeekableChannelSimpleMinIOTest.java +++ b/extensions/s3/src/test/java/io/deephaven/extensions/s3/S3SeekableChannelSimpleMinIOTest.java @@ -5,7 +5,7 @@ import io.deephaven.extensions.s3.S3Instructions.Builder; import io.deephaven.extensions.s3.testlib.SingletonContainers.MinIO; -import io.deephaven.stats.util.OSUtil; +import io.deephaven.base.OSUtil; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; diff --git a/log-factory/gradle.properties b/log-factory/gradle.properties index c186bbfdde1..9e8588f2620 100644 --- a/log-factory/gradle.properties +++ b/log-factory/gradle.properties @@ -1 +1,2 @@ io.deephaven.project.ProjectType=JAVA_PUBLIC +languageLevel=8 diff --git a/log-factory/src/main/java/io/deephaven/internal/log/Bootstrap.java b/log-factory/src/main/java/io/deephaven/internal/log/Bootstrap.java index 9c2f1ecc6c2..f68fe755911 100644 --- a/log-factory/src/main/java/io/deephaven/internal/log/Bootstrap.java +++ b/log-factory/src/main/java/io/deephaven/internal/log/Bootstrap.java @@ -8,9 +8,11 @@ public final class Bootstrap { public static boolean isQuiet() { - return viaProperty() - .or(Bootstrap::viaEnvironment) - .map(Boolean::parseBoolean) + Optional optional = viaProperty(); + if (!optional.isPresent()) { + optional = viaEnvironment(); + } + return optional.map(Boolean::parseBoolean) .orElse(false); } diff --git a/py/embedded-server/build.gradle b/py/embedded-server/build.gradle index 2479a7d0b38..4e6c2f5d48e 100644 --- a/py/embedded-server/build.gradle +++ b/py/embedded-server/build.gradle @@ -25,6 +25,8 @@ dependencies { api(project(':Integrations')) { because 'downstream dagger compile' } + + implementation project(":util-processenvironment") } def testEmbeddedServer = Docker.registerDockerTask(project, 'testEmbeddedServer') { diff --git a/py/embedded-server/java-runtime/build.gradle b/py/embedded-server/java-runtime/build.gradle index 1fadd12b881..a2b8bc35d30 100644 --- a/py/embedded-server/java-runtime/build.gradle +++ b/py/embedded-server/java-runtime/build.gradle @@ -10,6 +10,8 @@ configurations { dependencies { implementation project(':server-jetty') + implementation project(":util-processenvironment") + implementation project(":util-thread") implementation libs.dagger annotationProcessor libs.dagger.compiler diff --git a/server/build.gradle b/server/build.gradle index cc56e562501..e7ae21ab845 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -4,6 +4,8 @@ plugins { } dependencies { + implementation project(":util-thread") + implementation project(":util-processenvironment") implementation project(':authentication') implementation project(':authorization') implementation project(':engine-table') diff --git a/server/jetty-app-custom/build.gradle b/server/jetty-app-custom/build.gradle index b66da129e31..1ef2bbe9a78 100644 --- a/server/jetty-app-custom/build.gradle +++ b/server/jetty-app-custom/build.gradle @@ -9,6 +9,7 @@ configurations { dependencies { implementation project(':server-jetty') + implementation project(":util-thread") implementation libs.dagger annotationProcessor libs.dagger.compiler diff --git a/server/jetty/build.gradle b/server/jetty/build.gradle index a02fb606602..0004397cf48 100644 --- a/server/jetty/build.gradle +++ b/server/jetty/build.gradle @@ -10,6 +10,9 @@ dependencies { api(project(':Integrations')) { because 'downstream dagger compile' } + + implementation project(":util-thread") + runtimeOnly(project(':web')) implementation libs.dagger diff --git a/server/netty/build.gradle b/server/netty/build.gradle index c67dec9c86b..e771ab1c075 100644 --- a/server/netty/build.gradle +++ b/server/netty/build.gradle @@ -11,6 +11,8 @@ dependencies { because 'downstream dagger compile' } + implementation project(":util-thread") + implementation libs.dagger annotationProcessor libs.dagger.compiler diff --git a/server/test-utils/build.gradle b/server/test-utils/build.gradle index d9b0e11bb0a..54133d912b0 100644 --- a/server/test-utils/build.gradle +++ b/server/test-utils/build.gradle @@ -5,6 +5,7 @@ plugins { } dependencies { + implementation project(":util-thread") implementation project(':Base') implementation project(':authentication') implementation project(':authorization') diff --git a/settings.gradle b/settings.gradle index bbca0b514b7..d2bad06572b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -352,6 +352,15 @@ project(':util-function').projectDir = file('Util/function') include(':util-channel') project(':util-channel').projectDir = file('Util/channel') +include(':util-processenvironment') +project(':util-processenvironment').projectDir = file('Util/processenvironment') + +include(':util-thread') +project(':util-thread').projectDir = file('Util/thread') + +include(':util-pool') +project(':util-pool').projectDir = file('Util/pool') + include(':deephaven-jpy-config') project(':deephaven-jpy-config').projectDir = file('py/jpy-config')