diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5cd8674215..0ee2ecbcd5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -692,7 +692,7 @@ jobs: ICEBERG_MAIN_REPOSITORY: apache/iceberg ICEBERG_MAIN_BRANCH: master ICEBERG_PATCH_REPOSITORY: snazy/iceberg - ICEBERG_PATCH_BRANCH: iceberg-nesqueit + ICEBERG_PATCH_BRANCH: iceberg-nesqueit-next SPARK_LOCAL_IP: localhost steps: - name: Prepare Git diff --git a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieApiVersion.java b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieApiVersion.java index f2b5c3a05d7..692f9e3e306 100644 --- a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieApiVersion.java +++ b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieApiVersion.java @@ -41,7 +41,7 @@ public URI resolve(URI base) { return base.resolve(getPathElement()); } - public NessieApiV1 build(NessieClientBuilder builder) { + public NessieApiV1 build(NessieClientBuilder builder) { return builder.build(clientApiClass); } } diff --git a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientCustomizer.java b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientCustomizer.java index 2ca258a81e1..e035d0ddb84 100644 --- a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientCustomizer.java +++ b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientCustomizer.java @@ -26,5 +26,5 @@ @FunctionalInterface public interface NessieClientCustomizer { - NessieClientBuilder configure(NessieClientBuilder builder, NessieApiVersion apiVersion); + NessieClientBuilder configure(NessieClientBuilder builder, NessieApiVersion apiVersion); } diff --git a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientNameResolver.java b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientNameResolver.java new file mode 100644 index 00000000000..366f66acae2 --- /dev/null +++ b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientNameResolver.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.ext; + +import java.util.HashMap; +import java.util.Map; +import org.projectnessie.client.NessieClientBuilder; +import org.projectnessie.client.NessieConfigConstants; + +/** + * Test classes that implement this interface can specify a different Nessie client by {@linkplain + * NessieClientBuilder#name() its name} and optionally a set of configuration options. + */ +@FunctionalInterface +public interface NessieClientNameResolver { + String nessieClientName(); + + /** + * When overriding this function, make sure to use the map of this implementation to include the + * {@link #nessieClientName()}. + */ + default Map mainNessieClientConfigMap() { + Map mainConfig = new HashMap<>(); + mainConfig.put(NessieConfigConstants.CONF_NESSIE_CLIENT_NAME, nessieClientName()); + return mainConfig; + } +} diff --git a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientResolver.java b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientResolver.java index c8ecd617294..cdd4105566b 100644 --- a/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientResolver.java +++ b/api/client-testextension/src/main/java/org/projectnessie/client/ext/NessieClientResolver.java @@ -15,6 +15,7 @@ */ package org.projectnessie.client.ext; +import static org.projectnessie.client.NessieClientBuilder.createClientBuilderFromSystemSettings; import static org.projectnessie.client.ext.MultiVersionApiTest.apiVersion; import java.io.Serializable; @@ -30,8 +31,10 @@ import org.junit.platform.commons.util.AnnotationUtils; import org.projectnessie.client.NessieClientBuilder; import org.projectnessie.client.api.NessieApiV1; -import org.projectnessie.client.http.HttpClientBuilder; +import org.projectnessie.client.config.NessieClientConfigSource; +import org.projectnessie.client.config.NessieClientConfigSources; import org.projectnessie.client.http.HttpResponseFactory; +import org.projectnessie.client.http.NessieHttpClientBuilder; /** * A base class for extensions that manage a Nessie test execution environment. This class injects @@ -89,6 +92,15 @@ public Object resolveParameter( } private NessieClientFactory clientFactoryForContext(ExtensionContext extensionContext) { + NessieClientConfigSource mainConfigSource = + extensionContext + .getTestInstance() + .filter(t -> t instanceof NessieClientNameResolver) + .map(NessieClientNameResolver.class::cast) + .map(NessieClientNameResolver::mainNessieClientConfigMap) + .map(NessieClientConfigSources::mapConfigSource) + .orElseGet(NessieClientConfigSources::emptyConfigSource); + NessieApiVersion apiVersion = apiVersion(extensionContext); URI uri = resolvedNessieUri(extensionContext); List customizers = @@ -118,7 +130,7 @@ private NessieClientFactory clientFactoryForContext(ExtensionContext extensionCo return builder; }; - return new ClientFactory(uri, apiVersion, responseFactoryClass) { + return new ClientFactory(uri, mainConfigSource, apiVersion, responseFactoryClass) { @Nonnull @jakarta.annotation.Nonnull @Override // Note: this object is not serializable @@ -132,19 +144,23 @@ public NessieApiV1 make(NessieClientCustomizer customizer) { // We use a serializable impl. here as a workaround for @QuarkusTest instances, whose parameters // are deep-cloned by the Quarkus test extension. - return new ClientFactory(uri, apiVersion, responseFactoryClass); + return new ClientFactory(uri, mainConfigSource, apiVersion, responseFactoryClass); } private static class ClientFactory implements NessieClientFactory, Serializable { + private final URI resolvedUri; + private final NessieClientConfigSource mainConfigSource; private final NessieApiVersion apiVersion; private final Class responseFactoryClass; private ClientFactory( URI nessieUri, + NessieClientConfigSource mainConfigSource, NessieApiVersion apiVersion, Class responseFactoryClass) { this.resolvedUri = nessieUri; + this.mainConfigSource = mainConfigSource; this.apiVersion = apiVersion; this.responseFactoryClass = responseFactoryClass; } @@ -158,17 +174,19 @@ public NessieApiVersion apiVersion() { @jakarta.annotation.Nonnull @Override public NessieApiV1 make(NessieClientCustomizer customizer) { - HttpClientBuilder clientBuilder = HttpClientBuilder.builder().withUri(resolvedUri); + NessieClientBuilder clientBuilder = + createClientBuilderFromSystemSettings(mainConfigSource).withUri(resolvedUri); if (responseFactoryClass != null) { try { clientBuilder = - clientBuilder.withResponseFactory( - responseFactoryClass.getDeclaredConstructor().newInstance()); + clientBuilder + .asInstanceOf(NessieHttpClientBuilder.class) + .withResponseFactory(responseFactoryClass.getDeclaredConstructor().newInstance()); } catch (Exception e) { throw new RuntimeException(e); } } - NessieClientBuilder builder = customizer.configure(clientBuilder, apiVersion); + NessieClientBuilder builder = customizer.configure(clientBuilder, apiVersion); return apiVersion.build(builder); } } diff --git a/api/client/src/main/java/org/projectnessie/client/NessieClientBuilder.java b/api/client/src/main/java/org/projectnessie/client/NessieClientBuilder.java index bf7eeb20f77..46ab6dd88f9 100644 --- a/api/client/src/main/java/org/projectnessie/client/NessieClientBuilder.java +++ b/api/client/src/main/java/org/projectnessie/client/NessieClientBuilder.java @@ -15,25 +15,73 @@ */ package org.projectnessie.client; +import static org.projectnessie.client.NessieConfigConstants.CONF_CONNECT_TIMEOUT; +import static org.projectnessie.client.NessieConfigConstants.CONF_ENABLE_API_COMPATIBILITY_CHECK; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_CLIENT_BUILDER_IMPL; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_CLIENT_NAME; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_DISABLE_COMPRESSION; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SNI_HOSTS; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SNI_MATCHER; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SSL_CIPHER_SUITES; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SSL_PROTOCOLS; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_TRACING; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_URI; +import static org.projectnessie.client.NessieConfigConstants.CONF_READ_TIMEOUT; +import static org.projectnessie.client.config.NessieClientConfigSources.defaultConfigSources; +import static org.projectnessie.client.config.NessieClientConfigSources.emptyConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.systemPropertiesConfigSource; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.ServiceLoader; import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; import org.projectnessie.client.api.NessieApi; import org.projectnessie.client.auth.NessieAuthentication; +import org.projectnessie.client.auth.NessieAuthenticationProvider; +import org.projectnessie.client.config.NessieClientConfigSource; +import org.projectnessie.client.config.NessieClientConfigSources; -/** - * {@link NessieApi} builder interface. - * - * @param concrete nessie-client builder type - */ -public interface NessieClientBuilder> { +/** {@link NessieApi} builder interface. */ +public interface NessieClientBuilder { + + /** + * The name of the Nessie client implementation, for example {@code HTTP} for the HTTP/REST + * client. + */ + String name(); + + /** + * Priority ordinal used to select the most relevant {@link NessieClientBuilder} implementation. + */ + int priority(); + + I asInstanceOf(Class builderInterfaceType); + + @CanIgnoreReturnValue + NessieClientBuilder withApiCompatibilityCheck(boolean enable); /** * Same semantics as {@link #fromConfig(Function)}, uses the system properties. * * @return {@code this} * @see #fromConfig(Function) + * @deprecated Use {@link #fromConfig(Function)} with config sources from {@link + * NessieClientConfigSources}, preferably {@link + * NessieClientConfigSources#defaultConfigSources()}. */ - IMPL fromSystemProperties(); + @CanIgnoreReturnValue + @Deprecated + NessieClientBuilder fromSystemProperties(); /** * Configure this HttpClientBuilder instance using a configuration object and standard Nessie @@ -45,10 +93,11 @@ public interface NessieClientBuilder> { * * @param configuration The function that returns a configuration value for a configuration key. * @return {@code this} - * @see #fromSystemProperties() + * @see NessieClientConfigSources * @see #withAuthenticationFromConfig(Function) */ - IMPL fromConfig(Function configuration); + @CanIgnoreReturnValue + NessieClientBuilder fromConfig(Function configuration); /** * Configure only authentication in this HttpClientBuilder instance using a configuration object @@ -59,7 +108,8 @@ public interface NessieClientBuilder> { * @return {@code this} * @see #fromConfig(Function) */ - IMPL withAuthenticationFromConfig(Function configuration); + @CanIgnoreReturnValue + NessieClientBuilder withAuthenticationFromConfig(Function configuration); /** * Sets the {@link NessieAuthentication} instance to be used. @@ -67,7 +117,11 @@ public interface NessieClientBuilder> { * @param authentication authentication for this client * @return {@code this} */ - IMPL withAuthentication(NessieAuthentication authentication); + @CanIgnoreReturnValue + NessieClientBuilder withAuthentication(NessieAuthentication authentication); + + @CanIgnoreReturnValue + NessieClientBuilder withTracing(boolean tracing); /** * Set the Nessie server URI. A server URI must be configured. @@ -75,7 +129,8 @@ public interface NessieClientBuilder> { * @param uri server URI * @return {@code this} */ - IMPL withUri(URI uri); + @CanIgnoreReturnValue + NessieClientBuilder withUri(URI uri); /** * Convenience method for {@link #withUri(URI)} taking a string. @@ -83,9 +138,31 @@ public interface NessieClientBuilder> { * @param uri server URI * @return {@code this} */ - default IMPL withUri(String uri) { - return withUri(URI.create(uri)); - } + @CanIgnoreReturnValue + NessieClientBuilder withUri(String uri); + + /** Sets the read-timeout in milliseconds for remote requests. */ + @CanIgnoreReturnValue + NessieClientBuilder withReadTimeout(int readTimeoutMillis); + + /** Sets the connect-timeout in milliseconds for remote requests. */ + @CanIgnoreReturnValue + NessieClientBuilder withConnectionTimeout(int connectionTimeoutMillis); + + /** Disables compression for remote requests. */ + @CanIgnoreReturnValue + NessieClientBuilder withDisableCompression(boolean disableCompression); + + /** + * Optionally configure a specific {@link SSLContext}, currently only the Java 11+ accepts this + * option. + */ + @CanIgnoreReturnValue + NessieClientBuilder withSSLContext(SSLContext sslContext); + + /** Optionally configure specific {@link SSLParameters}. */ + @CanIgnoreReturnValue + NessieClientBuilder withSSLParameters(SSLParameters sslParameters); /** * Builds a new {@link NessieApi}. @@ -93,4 +170,253 @@ default IMPL withUri(String uri) { * @return A new {@link NessieApi}. */ API build(Class apiContract); + + /** + * Constructs a client builder instance using config settings from Java system properties, process + * environment, Nessie client config file {@code ~/.config/nessie/nessie-client.properties}, + * dot-env file {@code ~/.env}. + */ + static NessieClientBuilder createClientBuilderFromSystemSettings() { + return createClientBuilderFromSystemSettings(emptyConfigSource()); + } + + static NessieClientBuilder createClientBuilderFromSystemSettings( + NessieClientConfigSource mainConfigSource) { + NessieClientConfigSource configSource = mainConfigSource.fallbackTo(defaultConfigSources()); + String clientName = configSource.getValue(CONF_NESSIE_CLIENT_NAME); + @SuppressWarnings("deprecation") + String clientBuilderImpl = configSource.getValue(CONF_NESSIE_CLIENT_BUILDER_IMPL); + return createClientBuilder(clientName, clientBuilderImpl).fromConfig(configSource.asFunction()); + } + + /** + * Returns the Nessie client builder that matches the requested client name or client builder + * implementation class. + * + *

Nessie clients are discovered using Java's {@link ServiceLoader service loader} mechanism. + * + *

The selection mechanism uses the given {@link NessieClientBuilder#name() Nessie client name} + * or Nessie client builder implementation class name to select the client builder from the list + * of available implementations. + * + *

If neither a name nor an implementation class are specified, aka both parameters are {@code + * null}, the Nessie client builder with the highest {@link NessieClientBuilder#priority()} will + * be returned. + * + *

Either the name or the implementation class should be specified. Specifying both is + * discouraged. + * + * @param clientName the name of the Nessie client, as returned by {@link + * NessieClientBuilder#name()}, or {@code null} + * @param clientBuilderImpl the class that implements the Nessie client builder, or {@code null} + * @return Nessie client builder for the requested name or implementation class. + * @throws IllegalArgumentException if no Nessie client matching the requested name and/or + * implementation class could be found + */ + @Nonnull + @jakarta.annotation.Nonnull + static NessieClientBuilder createClientBuilder(String clientName, String clientBuilderImpl) { + ServiceLoader implementations = + ServiceLoader.load(NessieClientBuilder.class); + + List builders = new ArrayList<>(); + for (NessieClientBuilder clientBuilder : implementations) { + builders.add(clientBuilder); + if (clientBuilder.name().equals(clientName)) { + if (clientBuilderImpl != null + && !clientBuilderImpl.isEmpty() + && !clientBuilder.getClass().getName().equals(clientBuilderImpl)) { + throw new IllegalArgumentException( + "Requested client named " + + clientName + + " does not match requested client builder implementation " + + clientBuilderImpl); + } + return clientBuilder; + } + if (clientBuilder.getClass().getName().equals(clientBuilderImpl)) { + if (clientName != null && !clientName.isEmpty()) { + throw new IllegalArgumentException( + "Requested client builder implementation " + + clientBuilderImpl + + " does not match requested client named " + + clientName); + } + return clientBuilder; + } + } + + if (clientBuilderImpl != null && clientName == null) { + // Fallback mechanism for old code that refers to classes that are not mentioned in the file + // META-INF/services/org.projectnessie.client.NessieClientBuilder, tested via the + // compatibility tests, so it's possible that old code still uses that mechanism. + try { + return (NessieClientBuilder) + Class.forName(clientBuilderImpl).getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Cannot load Nessie client builder implementation class", e); + } + } + + if (clientBuilderImpl != null || clientName != null) { + String msg = "Requested Nessie client"; + msg += " named " + clientName; + if (clientBuilderImpl != null) { + msg += " or builder implementation class " + clientBuilderImpl; + } + throw new IllegalArgumentException(msg + " not found."); + } + + builders.sort(Comparator.comparingInt(NessieClientBuilder::priority)); + if (builders.isEmpty()) { + throw new IllegalStateException( + "No implementation of " + NessieClientBuilder.class.getName() + " available"); + } + // Return the client builder with the _highest_ priority value + return builders.get(builders.size() - 1); + } + + /** Convenience base class for implementations of {@link NessieClientBuilder}. */ + abstract class AbstractNessieClientBuilder implements NessieClientBuilder { + @Override + @Deprecated + public NessieClientBuilder fromSystemProperties() { + return fromConfig(systemPropertiesConfigSource().asFunction()); + } + + @Override + public NessieClientBuilder fromConfig(Function configuration) { + String uri = configuration.apply(CONF_NESSIE_URI); + if (uri != null) { + withUri(URI.create(uri)); + } + + withAuthenticationFromConfig(configuration); + + String s = configuration.apply(CONF_NESSIE_TRACING); + if (s != null) { + withTracing(Boolean.parseBoolean(s)); + } + s = configuration.apply(CONF_CONNECT_TIMEOUT); + if (s != null) { + withConnectionTimeout(Integer.parseInt(s)); + } + s = configuration.apply(CONF_READ_TIMEOUT); + if (s != null) { + withReadTimeout(Integer.parseInt(s)); + } + s = configuration.apply(CONF_NESSIE_DISABLE_COMPRESSION); + if (s != null) { + withDisableCompression(Boolean.parseBoolean(s)); + } + + SSLParameters sslParameters = new SSLParameters(); + boolean hasSslParameters = false; + s = configuration.apply(CONF_NESSIE_SSL_CIPHER_SUITES); + if (s != null) { + hasSslParameters = true; + sslParameters.setCipherSuites( + Arrays.stream(s.split(",")) + .map(String::trim) + .filter(v -> !v.isEmpty()) + .toArray(String[]::new)); + } + s = configuration.apply(CONF_NESSIE_SSL_PROTOCOLS); + if (s != null) { + hasSslParameters = true; + sslParameters.setProtocols( + Arrays.stream(s.split(",")) + .map(String::trim) + .filter(v -> !v.isEmpty()) + .toArray(String[]::new)); + } + s = configuration.apply(CONF_NESSIE_SNI_HOSTS); + if (s != null) { + hasSslParameters = true; + sslParameters.setServerNames( + Arrays.stream(s.split(",")) + .map(String::trim) + .filter(v -> !v.isEmpty()) + .map(SNIHostName::new) + .collect(Collectors.toList())); + } + s = configuration.apply(CONF_NESSIE_SNI_MATCHER); + if (s != null) { + hasSslParameters = true; + sslParameters.setSNIMatchers(Collections.singletonList(SNIHostName.createSNIMatcher(s))); + } + if (hasSslParameters) { + withSSLParameters(sslParameters); + } + + s = configuration.apply(CONF_ENABLE_API_COMPATIBILITY_CHECK); + if (s != null) { + withApiCompatibilityCheck(Boolean.parseBoolean(s)); + } + + return this; + } + + @Override + public NessieClientBuilder withAuthenticationFromConfig( + Function configuration) { + withAuthentication(NessieAuthenticationProvider.fromConfig(configuration)); + return this; + } + + @Override + public NessieClientBuilder withUri(String uri) { + return withUri(URI.create(uri)); + } + + @Override + public I asInstanceOf(Class builderInterfaceType) { + return builderInterfaceType.cast(this); + } + + @Override + public NessieClientBuilder withApiCompatibilityCheck(boolean enable) { + return this; + } + + @Override + public NessieClientBuilder withAuthentication(NessieAuthentication authentication) { + return this; + } + + @Override + public NessieClientBuilder withTracing(boolean tracing) { + return this; + } + + @Override + public NessieClientBuilder withUri(URI uri) { + return this; + } + + @Override + public NessieClientBuilder withReadTimeout(int readTimeoutMillis) { + return this; + } + + @Override + public NessieClientBuilder withConnectionTimeout(int connectionTimeoutMillis) { + return this; + } + + @Override + public NessieClientBuilder withDisableCompression(boolean disableCompression) { + return this; + } + + @Override + public NessieClientBuilder withSSLContext(SSLContext sslContext) { + return this; + } + + @Override + public NessieClientBuilder withSSLParameters(SSLParameters sslParameters) { + return this; + } + } } diff --git a/api/client/src/main/java/org/projectnessie/client/NessieConfigConstants.java b/api/client/src/main/java/org/projectnessie/client/NessieConfigConstants.java index f85b3b2c079..828c43ff213 100644 --- a/api/client/src/main/java/org/projectnessie/client/NessieConfigConstants.java +++ b/api/client/src/main/java/org/projectnessie/client/NessieConfigConstants.java @@ -20,18 +20,21 @@ import org.projectnessie.client.auth.NessieAuthenticationProvider; /** Configuration constants for Nessie clients. */ +@SuppressWarnings("JavadocDeclaration") public final class NessieConfigConstants { /** Config property name ({@value #CONF_NESSIE_URI}) for the Nessie service URL. */ public static final String CONF_NESSIE_URI = "nessie.uri"; /** - * Config property name ({@value #CONF_NESSIE_USERNAME}) for the user name used for (basic) + * Config property name ({@value #CONF_NESSIE_USERNAME}) for the username used for (basic) * authentication. * * @deprecated "basic" HTTP authentication is not considered secure. Use {@link * #CONF_NESSIE_AUTH_TOKEN} instead. */ - @Deprecated public static final String CONF_NESSIE_USERNAME = "nessie.authentication.username"; + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + public static final String CONF_NESSIE_USERNAME = "nessie.authentication.username"; /** * Config property name ({@value #CONF_NESSIE_PASSWORD}) for the password used for (basic) @@ -40,7 +43,9 @@ public final class NessieConfigConstants { * @deprecated "basic" HTTP authentication is not considered secure. Use {@link * #CONF_NESSIE_AUTH_TOKEN} instead. */ - @Deprecated public static final String CONF_NESSIE_PASSWORD = "nessie.authentication.password"; + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated + public static final String CONF_NESSIE_PASSWORD = "nessie.authentication.password"; /** * Config property name ({@value #CONF_NESSIE_AUTH_TOKEN}) for the token used for (bearer) @@ -238,10 +243,18 @@ public final class NessieConfigConstants { public static final String CONF_NESSIE_DISABLE_COMPRESSION = "nessie.transport.disable-compression"; + /** Config property name ({@value #CONF_NESSIE_CLIENT_NAME}) for custom client builder name. */ + public static final String CONF_NESSIE_CLIENT_NAME = "nessie.client-builder-name"; + /** * Config property name ({@value #CONF_NESSIE_CLIENT_BUILDER_IMPL}) for custom client builder * class name. + * + * @deprecated Prefer using Nessie client implementation names, configured via {@link + * #CONF_NESSIE_CLIENT_NAME}. */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated public static final String CONF_NESSIE_CLIENT_BUILDER_IMPL = "nessie.client-builder-impl"; /** diff --git a/api/client/src/main/java/org/projectnessie/client/api/NessieApiV2.java b/api/client/src/main/java/org/projectnessie/client/api/NessieApiV2.java index bde36677f41..9af54731203 100644 --- a/api/client/src/main/java/org/projectnessie/client/api/NessieApiV2.java +++ b/api/client/src/main/java/org/projectnessie/client/api/NessieApiV2.java @@ -15,6 +15,11 @@ */ package org.projectnessie.client.api; +import org.projectnessie.client.api.ns.ClientSideCreateNamespace; +import org.projectnessie.client.api.ns.ClientSideDeleteNamespace; +import org.projectnessie.client.api.ns.ClientSideGetMultipleNamespaces; +import org.projectnessie.client.api.ns.ClientSideGetNamespace; +import org.projectnessie.client.api.ns.ClientSideUpdateNamespace; import org.projectnessie.model.Reference; /** @@ -72,4 +77,35 @@ public interface NessieApiV2 extends NessieApiV1 { @Override @Deprecated DeleteBranchBuilder deleteBranch(); + + @Override + @Deprecated + default GetRefLogBuilder getRefLog() { + throw new UnsupportedOperationException("Reflog is not supported in API v2"); + } + + @Override + default GetNamespaceBuilder getNamespace() { + return new ClientSideGetNamespace(this); + } + + @Override + default GetMultipleNamespacesBuilder getMultipleNamespaces() { + return new ClientSideGetMultipleNamespaces(this); + } + + @Override + default CreateNamespaceBuilder createNamespace() { + return new ClientSideCreateNamespace(this); + } + + @Override + default DeleteNamespaceBuilder deleteNamespace() { + return new ClientSideDeleteNamespace(this); + } + + @Override + default UpdateNamespaceBuilder updateProperties() { + return new ClientSideUpdateNamespace(this); + } } diff --git a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideCreateNamespace.java b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideCreateNamespace.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideCreateNamespace.java rename to api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideCreateNamespace.java index 6be09b5d92d..f960377aa57 100644 --- a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideCreateNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideCreateNamespace.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.util.v2api; +package org.projectnessie.client.api.ns; import static org.projectnessie.error.ContentKeyErrorDetails.contentKeyErrorDetails; diff --git a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideDeleteNamespace.java b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideDeleteNamespace.java similarity index 99% rename from api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideDeleteNamespace.java rename to api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideDeleteNamespace.java index f351d89513b..6d0396af6cd 100644 --- a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideDeleteNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideDeleteNamespace.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.util.v2api; +package org.projectnessie.client.api.ns; import static org.projectnessie.error.ContentKeyErrorDetails.contentKeyErrorDetails; diff --git a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideGetMultipleNamespaces.java b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideGetMultipleNamespaces.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideGetMultipleNamespaces.java rename to api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideGetMultipleNamespaces.java index ace017adf89..6f5b6bb2667 100644 --- a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideGetMultipleNamespaces.java +++ b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideGetMultipleNamespaces.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.util.v2api; +package org.projectnessie.client.api.ns; import static java.lang.String.format; diff --git a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideGetNamespace.java b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideGetNamespace.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideGetNamespace.java rename to api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideGetNamespace.java index 1f309b9f293..0adc1eb535a 100644 --- a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideGetNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideGetNamespace.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.util.v2api; +package org.projectnessie.client.api.ns; import static org.projectnessie.error.ContentKeyErrorDetails.contentKeyErrorDetails; diff --git a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideUpdateNamespace.java b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideUpdateNamespace.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideUpdateNamespace.java rename to api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideUpdateNamespace.java index c4f7331ec48..2218181a381 100644 --- a/api/client/src/main/java/org/projectnessie/client/util/v2api/ClientSideUpdateNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/api/ns/ClientSideUpdateNamespace.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.util.v2api; +package org.projectnessie.client.api.ns; import java.util.HashMap; import org.projectnessie.client.api.NessieApiV2; diff --git a/api/client/src/main/java/org/projectnessie/client/auth/AwsAuthenticationProvider.java b/api/client/src/main/java/org/projectnessie/client/auth/AwsAuthenticationProvider.java index f9b6c60a611..8ad40fe7b34 100644 --- a/api/client/src/main/java/org/projectnessie/client/auth/AwsAuthenticationProvider.java +++ b/api/client/src/main/java/org/projectnessie/client/auth/AwsAuthenticationProvider.java @@ -86,7 +86,6 @@ private static class AwsAuthentication implements HttpAuthentication { private final Region region; private final AwsCredentialsProvider awsCredentialsProvider; - @SuppressWarnings({"QsPrivateBeanMembersInspection", "CdiInjectionPointsInspection"}) private AwsAuthentication(Region region, String profile) { this.region = region; @@ -110,7 +109,6 @@ private static class AwsHttpAuthenticationFilter implements RequestFilter { private final AwsCredentialsProvider awsCredentialsProvider; private final Region region; - @SuppressWarnings({"QsPrivateBeanMembersInspection", "CdiInjectionPointsInspection"}) private AwsHttpAuthenticationFilter(Region region, AwsCredentialsProvider credentialsProvider) { this.awsCredentialsProvider = credentialsProvider; this.objectMapper = diff --git a/api/client/src/main/java/org/projectnessie/client/builder/ResultStreamPaginator.java b/api/client/src/main/java/org/projectnessie/client/builder/ResultStreamPaginator.java deleted file mode 100644 index 721660f342f..00000000000 --- a/api/client/src/main/java/org/projectnessie/client/builder/ResultStreamPaginator.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2023 Dremio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.projectnessie.client.builder; - -import java.util.List; -import java.util.Spliterator; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; -import org.projectnessie.error.NessieNotFoundException; -import org.projectnessie.model.PaginatedResponse; - -/** - * Internal helper class to implement continuation token driven paging for a result stream. - * - * @param REST result page type - * @param entry type - */ -final class ResultStreamPaginator { - - @FunctionalInterface - interface Fetcher { - /** - * Fetch the first/next response. - * - * @param pagingToken paging continuation token - * @return response object - * @throws NessieNotFoundException if the ref does not exist - */ - R fetch(String pagingToken) throws NessieNotFoundException; - } - - private final Function> entriesFromResponse; - private final Fetcher fetcher; - - ResultStreamPaginator(Function> entriesFromResponse, Fetcher fetcher) { - this.entriesFromResponse = entriesFromResponse; - this.fetcher = fetcher; - } - - /** - * Constructs the stream that uses paging under the covers. - * - *

This implementation fetches the first page eagerly to propagate {@link - * NessieNotFoundException}. - * - * @return stream of entries - * @throws NessieNotFoundException propagated from {@link Fetcher#fetch(String)} - */ - Stream generateStream() throws NessieNotFoundException { - R firstPage = fetcher.fetch(null); - Spliterator spliterator = - new Spliterator() { - private String pageToken; - private boolean eof; - private int offsetInPage; - private R currentPage = firstPage; - - @Override - public boolean tryAdvance(Consumer action) { - if (eof) { - return false; - } - - if (currentPage != null - && entriesFromResponse.apply(currentPage).size() == offsetInPage) { - // Already returned the last entry in the current page - if (!currentPage.isHasMore()) { - eof = true; - return false; - } - pageToken = currentPage.getToken(); - currentPage = null; - offsetInPage = 0; - } - - if (currentPage == null) { - try { - currentPage = fetcher.fetch(pageToken); - offsetInPage = 0; - // an empty returned page is probably an error, let's assume something went wrong - if (entriesFromResponse.apply(currentPage).isEmpty()) { - if (currentPage.isHasMore()) { - throw new IllegalStateException( - "Backend returned empty page, but indicates there are more results"); - } - eof = true; - return false; - } - } catch (NessieNotFoundException e) { - eof = true; - throw new RuntimeException(e); - } - } - - action.accept(entriesFromResponse.apply(currentPage).get(offsetInPage++)); - return true; - } - - @Override - public Spliterator trySplit() { - return null; - } - - @Override - public long estimateSize() { - return 0; - } - - @Override - public int characteristics() { - return 0; - } - }; - return StreamSupport.stream(spliterator, false); - } -} diff --git a/api/client/src/main/java/org/projectnessie/client/builder/StreamingUtil.java b/api/client/src/main/java/org/projectnessie/client/builder/StreamingUtil.java index 2f4f509d078..04696f970ad 100644 --- a/api/client/src/main/java/org/projectnessie/client/builder/StreamingUtil.java +++ b/api/client/src/main/java/org/projectnessie/client/builder/StreamingUtil.java @@ -16,26 +16,15 @@ package org.projectnessie.client.builder; import java.util.List; -import java.util.OptionalInt; +import java.util.Spliterator; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Stream; -import javax.validation.constraints.NotNull; -import org.projectnessie.client.api.GetAllReferencesBuilder; -import org.projectnessie.client.api.GetCommitLogBuilder; -import org.projectnessie.client.api.GetEntriesBuilder; -import org.projectnessie.client.api.GetRefLogBuilder; +import java.util.stream.StreamSupport; import org.projectnessie.client.api.NessieApiV1; import org.projectnessie.client.api.PagingBuilder; import org.projectnessie.error.NessieNotFoundException; -import org.projectnessie.model.CommitMeta; -import org.projectnessie.model.EntriesResponse; -import org.projectnessie.model.EntriesResponse.Entry; -import org.projectnessie.model.LogResponse; -import org.projectnessie.model.LogResponse.LogEntry; import org.projectnessie.model.PaginatedResponse; -import org.projectnessie.model.RefLogResponse; -import org.projectnessie.model.Reference; -import org.projectnessie.model.ReferencesResponse; /** * Helper and utility methods around streaming of {@link NessieApiV1} et al. @@ -49,149 +38,108 @@ private StreamingUtil() { // intentionally blank } - /** - * Default implementation to return a stream of references, functionally equivalent to calling - * {@link NessieApiV1#getAllReferences()} with manual paging. - * - * @param api The {@link NessieApiV1} to use - * @param builderCustomizer a Function that takes and returns {@link GetAllReferencesBuilder} that - * is passed to the caller of this method. It allows the caller to customize the - * GetAllReferencesBuilder with additional parameters (e.g.: filter). - * @param pageSizeHint A hint sent to the Nessie server to return no more than this amount of - * entries in a single response, the server may decide to ignore this hint or return more or - * less results. Use {@link OptionalInt#empty()} to omit the hint. - * @return stream of {@link Reference} objects - * @deprecated Use {@link GetAllReferencesBuilder#stream()} - */ - @Deprecated - public static Stream getAllReferencesStream( - @NotNull @jakarta.validation.constraints.NotNull NessieApiV1 api, - @NotNull @jakarta.validation.constraints.NotNull - Function builderCustomizer, - @NotNull @jakarta.validation.constraints.NotNull OptionalInt pageSizeHint) - throws NessieNotFoundException { - GetAllReferencesBuilder builder = builderCustomizer.apply(api.getAllReferences()); - Integer pageSize = pageSizeHint.isPresent() ? pageSizeHint.getAsInt() : null; - return new ResultStreamPaginator<>( - ReferencesResponse::getReferences, - token -> builderWithPaging(builder, pageSize, token).get()) - .generateStream(); + @FunctionalInterface + public interface PageFetcher { + R fetchPage(String pageToken) throws NessieNotFoundException; } - /** - * Default implementation to return a stream of objects for a ref, functionally equivalent to - * calling {@link NessieApiV1#getEntries()} with manual paging. - * - *

The {@link Stream} returned by {@code getEntriesStream(ref, OptionalInt.empty())}, if not - * limited, returns all commit-log entries. - * - * @param api The {@link NessieApiV1} to use - * @param builderCustomizer a Function that takes and returns {@link GetEntriesBuilder} that is - * passed to the caller of this method. It allows the caller to customize the - * GetEntriesBuilder with additional parameters (e.g.: hashOnRef, filter). - * @param pageSizeHint A hint sent to the Nessie server to return no more than this amount of - * entries in a single response, the server may decide to ignore this hint or return more or - * less results. Use {@link OptionalInt#empty()} to omit the hint. - * @return stream of {@link Entry} objects - * @deprecated Use {@link GetEntriesBuilder#stream()} - */ - @Deprecated - public static Stream getEntriesStream( - @NotNull @jakarta.validation.constraints.NotNull NessieApiV1 api, - @NotNull @jakarta.validation.constraints.NotNull - Function builderCustomizer, - @NotNull @jakarta.validation.constraints.NotNull OptionalInt pageSizeHint) + public static Stream generateStream( + Function> entriesExtractor, PageFetcher pageFetcher) throws NessieNotFoundException { - GetEntriesBuilder builder = builderCustomizer.apply(api.getEntries()); - Integer pageSize = pageSizeHint.isPresent() ? pageSizeHint.getAsInt() : null; - return new ResultStreamPaginator<>( - EntriesResponse::getEntries, token -> builderWithPaging(builder, pageSize, token).get()) - .generateStream(); + return new ResultStreamPaginator<>(entriesExtractor, pageFetcher).generateStream(); } /** - * Default implementation to return a stream of commit-log entries, functionally equivalent to - * calling {@link NessieApiV1#getCommitLog()} with manual paging. + * Internal helper class to implement continuation token driven paging for a result stream. * - *

The {@link Stream} returned by {@code getCommitLogStream(ref, OptionalInt.empty())}, if not - * limited, returns all commit-log entries. - * - * @param api The {@link NessieApiV1} to use - * @param builderCustomizer a Function that takes and returns {@link GetCommitLogBuilder} that is - * passed to the caller of this method. It allows the caller to customize the - * GetCommitLogBuilder with additional parameters (e.g.: hashOnRef, untilHash, filter, - * fetchOption). - * @param pageSizeHint A hint sent to the Nessie server to return no more than this amount of - * entries in a single response, the server may decide to ignore this hint or return more or - * less results. Use {@link OptionalInt#empty()} to omit the hint. - * @return stream of {@link CommitMeta} objects - * @deprecated Use {@link GetCommitLogBuilder#stream()} + * @param REST result page type + * @param entry type */ - @Deprecated - public static Stream getCommitLogStream( - @NotNull @jakarta.validation.constraints.NotNull NessieApiV1 api, - @NotNull @jakarta.validation.constraints.NotNull - Function builderCustomizer, - @NotNull @jakarta.validation.constraints.NotNull OptionalInt pageSizeHint) - throws NessieNotFoundException { - GetCommitLogBuilder builder = builderCustomizer.apply(api.getCommitLog()); - Integer pageSize = pageSizeHint.isPresent() ? pageSizeHint.getAsInt() : null; - return new ResultStreamPaginator<>( - LogResponse::getLogEntries, token -> builderWithPaging(builder, pageSize, token).get()) - .generateStream(); - } + static final class ResultStreamPaginator { - /** - * Default implementation to return a stream of reflog entries, functionally equivalent to calling - * {@link NessieApiV1#getRefLog()} with manual paging. - * - *

The {@link Stream} returned by {@code getReflogStream(OptionalInt.empty())}, if not limited, - * returns all reflog entries. - * - * @param api The {@link NessieApiV1} to use - * @param builderCustomizer a Function that takes and returns {@link GetRefLogBuilder} that is - * passed to the caller of this method. It allows the caller to customize the GetRefLogBuilder - * with additional parameters (e.g.: fromHash, untilHash, filter). - * @param pageSizeHint A hint sent to the Nessie server to return no more than this amount of - * entries in a single response, the server may decide to ignore this hint or return more or - * less results. Use {@link OptionalInt#empty()} to omit the hint. - * @return stream of {@link RefLogResponse.RefLogResponseEntry} objects - * @deprecated Use {@link GetRefLogBuilder#stream()} - */ - @Deprecated - public static Stream getReflogStream( - @NotNull @jakarta.validation.constraints.NotNull NessieApiV1 api, - @NotNull @jakarta.validation.constraints.NotNull - Function builderCustomizer, - @NotNull @jakarta.validation.constraints.NotNull OptionalInt pageSizeHint) - throws NessieNotFoundException { - GetRefLogBuilder builder = builderCustomizer.apply(api.getRefLog()); - Integer pageSize = pageSizeHint.isPresent() ? pageSizeHint.getAsInt() : null; - return new ResultStreamPaginator<>( - RefLogResponse::getLogEntries, - token -> builderWithPaging(builder, pageSize, token).get()) - .generateStream(); - } + private final Function> entriesFromResponse; + private final PageFetcher fetcher; - private static > B builderWithPaging( - B builder, Integer pageSize, String token) { - if (pageSize != null) { - builder.maxRecords(pageSize); - } - if (token != null) { - builder.pageToken(token); + ResultStreamPaginator(Function> entriesFromResponse, PageFetcher fetcher) { + this.entriesFromResponse = entriesFromResponse; + this.fetcher = fetcher; } - return builder; - } - @FunctionalInterface - public interface NextPage { - R getPage(String pageToken) throws NessieNotFoundException; - } + /** + * Constructs the stream that uses paging under the covers. + * + *

This implementation fetches the first page eagerly to propagate {@link + * NessieNotFoundException}. + * + * @return stream of entries + * @throws NessieNotFoundException propagated from {@link PageFetcher#fetchPage(String)} + */ + Stream generateStream() throws NessieNotFoundException { + R firstPage = fetcher.fetchPage(null); + Spliterator spliterator = + new Spliterator() { + private String pageToken; + private boolean eof; + private int offsetInPage; + private R currentPage = firstPage; - public static Stream generateStream( - Function> entriesExtractor, NextPage pageFetcher) - throws NessieNotFoundException { - return new ResultStreamPaginator<>(entriesExtractor, pageFetcher::getPage).generateStream(); + @Override + public boolean tryAdvance(Consumer action) { + if (eof) { + return false; + } + + if (currentPage != null + && entriesFromResponse.apply(currentPage).size() == offsetInPage) { + // Already returned the last entry in the current page + if (!currentPage.isHasMore()) { + eof = true; + return false; + } + pageToken = currentPage.getToken(); + currentPage = null; + offsetInPage = 0; + } + + if (currentPage == null) { + try { + currentPage = fetcher.fetchPage(pageToken); + offsetInPage = 0; + // an empty returned page is probably an error, let's assume something went wrong + if (entriesFromResponse.apply(currentPage).isEmpty()) { + if (currentPage.isHasMore()) { + throw new IllegalStateException( + "Backend returned empty page, but indicates there are more results"); + } + eof = true; + return false; + } + } catch (NessieNotFoundException e) { + eof = true; + throw new RuntimeException(e); + } + } + + action.accept(entriesFromResponse.apply(currentPage).get(offsetInPage++)); + return true; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 0; + } + + @Override + public int characteristics() { + return 0; + } + }; + return StreamSupport.stream(spliterator, false); + } } } diff --git a/api/client/src/main/java/org/projectnessie/client/config/NessieClientConfigSource.java b/api/client/src/main/java/org/projectnessie/client/config/NessieClientConfigSource.java new file mode 100644 index 00000000000..d6409b44441 --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/config/NessieClientConfigSource.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.config; + +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** Helper functional interface adding a fallback-mechanism to chain multiple config-sources. */ +@FunctionalInterface +public interface NessieClientConfigSource { + @Nullable + @jakarta.annotation.Nullable + String getValue(@Nonnull @jakarta.annotation.Nonnull String key); + + @Nonnull + @jakarta.annotation.Nonnull + default NessieClientConfigSource fallbackTo( + @Nonnull @jakarta.annotation.Nonnull NessieClientConfigSource fallback) { + return k -> { + String v = getValue(k); + return v != null ? v : fallback.getValue(k); + }; + } + + @Nonnull + @jakarta.annotation.Nonnull + default Function asFunction() { + return this::getValue; + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/config/NessieClientConfigSources.java b/api/client/src/main/java/org/projectnessie/client/config/NessieClientConfigSources.java new file mode 100644 index 00000000000..24a56c7c193 --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/config/NessieClientConfigSources.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.config; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides functionality to retrieve configuration values from various sources, like Java + * properties, maps and environment variables. + * + *

Note that the current implementation does not need any additional libraries. If there is a + * need for advanced features like config encryption or expressions, the implementation might start + * using SmallRye-Config. + */ +public final class NessieClientConfigSources { + // Keep the anonymous classes, do not use lambdas, because Quarkus tests use xstream for deep + // cloning, which does not work with lambdas. + + private static final Logger LOGGER = LoggerFactory.getLogger(NessieClientConfigSources.class); + private static final NessieClientConfigSource EMPTY_CONFIG_SOURCE = + new NessieClientConfigSource() { + @Nullable + @jakarta.annotation.Nullable + @Override + public String getValue(@Nonnull @jakarta.annotation.Nonnull String key) { + return null; + } + }; + + private NessieClientConfigSources() {} + + /** + * Uses values from the {@code ~/.env} file. + * + *

Similar to the behavior of smallrye-config, the {@code ~/.env} file must be in the Java + * properties file format. + * + * @see #environmentFileConfigSource(Path) + * @see #propertyNameToEnvironmentName(String) + */ + public static NessieClientConfigSource dotEnvFileConfigSource() { + Path dotEnvFile = dotEnvFile(); + return environmentFileConfigSource(dotEnvFile); + } + + /** + * Uses values from the {@code ~/.config/nessie/nessie-client.properties} file. + * + * @see #systemPropertiesConfigSource() + * @see #propertiesFileConfigSource(Path) + * @see #propertiesConfigSource(Properties) + */ + public static NessieClientConfigSource nessieClientConfigFileConfigSource() { + Path propertiesFile = nessieClientConfigFile(); + return propertiesFileConfigSource(propertiesFile); + } + + /** + * Uses values from the system environment, keys follow the {@link + * #propertyNameToEnvironmentName(String) environment name mapping}. + * + * @see #environmentConfigSource(Map) + * @see #dotEnvFileConfigSource() + */ + public static NessieClientConfigSource systemEnvironmentConfigSource() { + return environmentConfigSource(System.getenv()); + } + + /** + * Uses values from the Java system properties of the process. + * + * @see #propertiesConfigSource(Properties) + */ + public static NessieClientConfigSource systemPropertiesConfigSource() { + return propertiesConfigSource(System.getProperties()); + } + + /** + * Uses values from the given file, where keys in the file follow the {@link + * #propertyNameToEnvironmentName(String) environment name mapping}. + * + *

Similar to the behavior of smallrye-config, the {@code .env} file must be in the Java + * properties file format. + * + * @see #dotEnvFileConfigSource() + * @see #propertyNameToEnvironmentName(String) + */ + public static NessieClientConfigSource environmentFileConfigSource(Path envFile) { + if (!Files.isRegularFile(envFile)) { + return EMPTY_CONFIG_SOURCE; + } + Properties props = loadProperties(envFile); + + return new NessieClientConfigSource() { + @Nullable + @jakarta.annotation.Nullable + @Override + public String getValue(@Nonnull @jakarta.annotation.Nonnull String key) { + String envName = propertyNameToEnvironmentName(key); + String v = props.getProperty(envName); + LOGGER.debug("Config value for key {} as {} retrieved from {}", key, envName, envFile); + return v; + } + }; + } + + /** + * Uses values from the given map, where keys in the file follow the {@link + * #propertyNameToEnvironmentName(String) environment name mapping}. + * + * @see #systemEnvironmentConfigSource() + * @see #environmentFileConfigSource(Path) + * @see #dotEnvFileConfigSource() + */ + public static NessieClientConfigSource environmentConfigSource(Map environment) { + return new NessieClientConfigSource() { + @Nullable + @jakarta.annotation.Nullable + @Override + public String getValue(@Nonnull @jakarta.annotation.Nonnull String key) { + String envName = propertyNameToEnvironmentName(key); + String v = environment.get(envName); + LOGGER.debug("Config value for key {} as {} retrieved from environment", key, envName); + return v; + } + }; + } + + /** + * Uses values from the given properties file. + * + * @see #propertiesConfigSource(Properties) + * @see #nessieClientConfigFileConfigSource() + */ + public static NessieClientConfigSource propertiesFileConfigSource(Path propertiesFile) { + if (!Files.isRegularFile(propertiesFile)) { + return EMPTY_CONFIG_SOURCE; + } + Properties props = loadProperties(propertiesFile); + + return new NessieClientConfigSource() { + @Nullable + @jakarta.annotation.Nullable + @Override + public String getValue(@Nonnull @jakarta.annotation.Nonnull String key) { + String v = props.getProperty(key); + LOGGER.debug("Config value for key {} retrieved from {}", key, propertiesFile); + return v; + } + }; + } + + /** + * Uses values from the given {@link Properties}. + * + * @see #systemPropertiesConfigSource() + * @see #mapConfigSource(Map) + */ + public static NessieClientConfigSource propertiesConfigSource(Properties properties) { + return new NessieClientConfigSource() { + @Nullable + @jakarta.annotation.Nullable + @Override + public String getValue(@Nonnull @jakarta.annotation.Nonnull String key) { + String v = properties.getProperty(key); + LOGGER.debug("Config value for key {} retrieved from properties", key); + return v; + } + }; + } + + /** + * Uses values from the given {@link Map}. + * + * @see #propertiesConfigSource(Properties) + */ + public static NessieClientConfigSource mapConfigSource(Map properties) { + return new NessieClientConfigSource() { + @Nullable + @jakarta.annotation.Nullable + @Override + public String getValue(@Nonnull @jakarta.annotation.Nonnull String key) { + String v = properties.get(key); + LOGGER.debug("Config value for key {} retrieved from map", key); + return v; + } + }; + } + + /** + * Converts a given property name to the "environment variable name syntax", using upper-case + * characters and converting {@code .} ("dot") and {@code -} ("minus") to {@code _} + * ("underscore"). + */ + public static String propertyNameToEnvironmentName(String propertyName) { + return propertyName.toUpperCase(Locale.ROOT).replace('.', '_').replace('-', '_'); + } + + /** + * Creates a configuration value retriever using reasonable default config sources. + * + *

Config values are retrieved from the following sources: + * + *

    + *
  1. Java system properties, see {@link #systemPropertiesConfigSource()} + *
  2. Process environment, see {@link #systemEnvironmentConfigSource()} + *
  3. {@code ~/.config/nessie/nessie-client-properties} file, see {@link + * #nessieClientConfigFileConfigSource()} + *
  4. {@code ~/.env} file, see {@link #dotEnvFileConfigSource()} + *
+ */ + public static NessieClientConfigSource defaultConfigSources() { + return systemPropertiesConfigSource() + .fallbackTo(systemEnvironmentConfigSource()) + .fallbackTo(nessieClientConfigFileConfigSource()) + .fallbackTo(dotEnvFileConfigSource()); + } + + public static NessieClientConfigSource emptyConfigSource() { + return EMPTY_CONFIG_SOURCE; + } + + static Path dotEnvFile() { + return Paths.get(System.getProperty("user.dir"), ".env"); + } + + static Path nessieClientConfigFile() { + return Paths.get( + System.getProperty("user.dir"), ".config", "nessie", "nessie-client.properties"); + } + + private static Properties loadProperties(Path propertiesFile) { + Properties props = new Properties(); + try (BufferedReader input = Files.newBufferedReader(propertiesFile)) { + props.load(input); + } catch (IOException e) { + throw new RuntimeException(e); + } + return props; + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpClient.java b/api/client/src/main/java/org/projectnessie/client/http/HttpClient.java index 0b6102d310c..9ca964660ea 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpClient.java +++ b/api/client/src/main/java/org/projectnessie/client/http/HttpClient.java @@ -18,19 +18,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.net.URI; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; -import org.projectnessie.client.NessieConfigConstants; -import org.projectnessie.client.http.impl.HttpRuntimeConfig; -import org.projectnessie.client.http.impl.HttpUtils; -import org.projectnessie.client.http.impl.jdk11.JavaHttpClient; -import org.projectnessie.client.http.impl.jdk8.UrlConnectionClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Simple Http client to make REST calls. @@ -50,7 +39,7 @@ enum Method { HttpRequest newRequest(); static Builder builder() { - return new Builder(); + return new HttpClientBuilderImpl(); } URI getBaseUri(); @@ -58,254 +47,79 @@ static Builder builder() { @Override void close(); - class Builder { - private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class); - - private URI baseUri; - private ObjectMapper mapper; - private Class jsonView; - private HttpResponseFactory responseFactory = HttpResponse::new; - private SSLContext sslContext; - private SSLParameters sslParameters; - private HttpAuthentication authentication; - private int readTimeoutMillis = - Integer.getInteger( - "sun.net.client.defaultReadTimeout", NessieConfigConstants.DEFAULT_READ_TIMEOUT_MILLIS); - private int connectionTimeoutMillis = - Integer.getInteger( - "sun.net.client.defaultConnectionTimeout", - NessieConfigConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS); - private boolean disableCompression; - private final List requestFilters = new ArrayList<>(); - private final List responseFilters = new ArrayList<>(); - private boolean http2Upgrade; - private String followRedirects; - private boolean forceUrlConnectionClient; - private int clientSpec = 2; - - private Builder() {} - - private Builder(Builder other) { - this.baseUri = other.baseUri; - this.mapper = other.mapper; - this.jsonView = other.jsonView; - this.responseFactory = other.responseFactory; - this.sslContext = other.sslContext; - this.sslParameters = other.sslParameters; - this.authentication = other.authentication; - this.readTimeoutMillis = other.readTimeoutMillis; - this.connectionTimeoutMillis = other.connectionTimeoutMillis; - this.disableCompression = other.disableCompression; - this.requestFilters.addAll(other.requestFilters); - this.responseFilters.addAll(other.responseFilters); - this.http2Upgrade = other.http2Upgrade; - this.followRedirects = other.followRedirects; - this.forceUrlConnectionClient = other.forceUrlConnectionClient; - this.clientSpec = other.clientSpec; - } - - /** Creates a (shallow) copy of this builder. */ - public Builder copy() { - return new Builder(this); - } - - public URI getBaseUri() { - return baseUri; - } + interface Builder { + Builder copy(); @CanIgnoreReturnValue - public Builder setClientSpec(int clientSpec) { - this.clientSpec = clientSpec; - return this; - } + Builder setClientSpec(int clientSpec); @CanIgnoreReturnValue - public Builder setBaseUri(URI baseUri) { - this.baseUri = baseUri; - return this; - } + Builder setBaseUri(URI baseUri); @CanIgnoreReturnValue - public Builder setDisableCompression(boolean disableCompression) { - this.disableCompression = disableCompression; - return this; - } + Builder setDisableCompression(boolean disableCompression); @CanIgnoreReturnValue - public Builder setObjectMapper(ObjectMapper mapper) { - this.mapper = mapper; - return this; - } + Builder setObjectMapper(ObjectMapper mapper); @CanIgnoreReturnValue - public Builder setJsonView(Class jsonView) { - this.jsonView = jsonView; - return this; - } + Builder setJsonView(Class jsonView); @CanIgnoreReturnValue - public Builder setResponseFactory(HttpResponseFactory responseFactory) { - this.responseFactory = responseFactory; - return this; - } + Builder setResponseFactory(HttpResponseFactory responseFactory); @CanIgnoreReturnValue - public Builder setSslContext(SSLContext sslContext) { - this.sslContext = sslContext; - return this; - } + Builder setSslContext(SSLContext sslContext); @CanIgnoreReturnValue - public Builder setSslParameters(SSLParameters sslParameters) { - this.sslParameters = sslParameters; - return this; - } + Builder setSslParameters(SSLParameters sslParameters); @CanIgnoreReturnValue - public Builder setAuthentication(HttpAuthentication authentication) { - this.authentication = authentication; - return this; - } + Builder setAuthentication(HttpAuthentication authentication); @CanIgnoreReturnValue - public Builder setHttp2Upgrade(boolean http2Upgrade) { - this.http2Upgrade = http2Upgrade; - return this; - } + Builder setHttp2Upgrade(boolean http2Upgrade); @CanIgnoreReturnValue - public Builder setFollowRedirects(String followRedirects) { - this.followRedirects = followRedirects; - return this; - } + Builder setFollowRedirects(String followRedirects); @CanIgnoreReturnValue - public Builder setForceUrlConnectionClient(boolean forceUrlConnectionClient) { - this.forceUrlConnectionClient = forceUrlConnectionClient; - return this; - } + Builder setForceUrlConnectionClient(boolean forceUrlConnectionClient); @CanIgnoreReturnValue - public Builder setReadTimeoutMillis(int readTimeoutMillis) { - this.readTimeoutMillis = readTimeoutMillis; - return this; - } + Builder setReadTimeoutMillis(int readTimeoutMillis); @CanIgnoreReturnValue - public Builder setConnectionTimeoutMillis(int connectionTimeoutMillis) { - this.connectionTimeoutMillis = connectionTimeoutMillis; - return this; - } + Builder setConnectionTimeoutMillis(int connectionTimeoutMillis); /** * Register a request filter. This filter will be run before the request starts and can modify * eg headers. */ @CanIgnoreReturnValue - public Builder addRequestFilter(RequestFilter filter) { - requestFilters.add(filter); - return this; - } + Builder addRequestFilter(RequestFilter filter); /** * Register a response filter. This filter will be run after the request finishes and can for * example handle error states. */ @CanIgnoreReturnValue - public Builder addResponseFilter(ResponseFilter filter) { - responseFilters.add(filter); - return this; - } + Builder addResponseFilter(ResponseFilter filter); @CanIgnoreReturnValue - Builder clearRequestFilters() { - requestFilters.clear(); - return this; - } + Builder clearRequestFilters(); @CanIgnoreReturnValue - Builder clearResponseFilters() { - responseFilters.clear(); - return this; - } + Builder clearResponseFilters(); /** * Add tracing to the client. This will load the opentracing libraries. It is not possible to * remove tracing once it is added. */ @CanIgnoreReturnValue - public Builder addTracing() { - try { - OpentelemetryTracing.addTracing(this); - } catch (NoClassDefFoundError e) { - LOGGER.warn( - "Failed to initialize tracing, the opentracing libraries are probably missing.", e); - } - return this; - } + public Builder addTracing(); /** Construct an HttpClient from builder settings. */ - public HttpClient build() { - HttpUtils.checkArgument( - baseUri != null, "Cannot construct Http client. Must have a non-null uri"); - HttpUtils.checkArgument( - mapper != null, "Cannot construct Http client. Must have a non-null object mapper"); - if (sslContext == null) { - try { - sslContext = SSLContext.getDefault(); - } catch (NoSuchAlgorithmException e) { - throw new HttpClientException( - "Cannot construct Http Client. Default SSL config is invalid.", e); - } - } - - if (authentication != null) { - authentication.applyToHttpClient(this); - } - - HttpRuntimeConfig config = - HttpRuntimeConfig.builder() - .baseUri(baseUri) - .mapper(mapper) - .jsonView(jsonView) - .responseFactory(responseFactory) - .readTimeoutMillis(readTimeoutMillis) - .connectionTimeoutMillis(connectionTimeoutMillis) - .isDisableCompression(disableCompression) - .sslContext(sslContext) - .sslParameters(sslParameters) - .authentication(authentication) - .addAllRequestFilters(requestFilters) - .addAllResponseFilters(responseFilters) - .isHttp11Only(!http2Upgrade) - .followRedirects(followRedirects) - .forceUrlConnectionClient(forceUrlConnectionClient) - .clientSpec(clientSpec) - .build(); - - return ImplSwitch.FACTORY.apply(config); - } - - static class ImplSwitch { - static final Function FACTORY; - - static { - Function factory; - try { - Class.forName("java.net.http.HttpClient"); - factory = - config -> - // Need the system property for tests, "normal" users can use standard - // configuration options. - Boolean.getBoolean("nessie.client.force-url-connection-client") - || config.forceUrlConnectionClient() - ? new UrlConnectionClient(config) - : new JavaHttpClient(config); - } catch (ClassNotFoundException e) { - factory = UrlConnectionClient::new; - } - FACTORY = factory; - } - } + HttpClient build(); } } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilder.java b/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilder.java index 3ec1fadab2a..9da1dcde5b5 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilder.java +++ b/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilder.java @@ -15,349 +15,130 @@ */ package org.projectnessie.client.http; -import static org.projectnessie.client.NessieConfigConstants.CONF_CONNECT_TIMEOUT; import static org.projectnessie.client.NessieConfigConstants.CONF_ENABLE_API_COMPATIBILITY_CHECK; -import static org.projectnessie.client.NessieConfigConstants.CONF_FORCE_URL_CONNECTION_CLIENT; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_DISABLE_COMPRESSION; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_HTTP_2; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_HTTP_REDIRECT; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SNI_HOSTS; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SNI_MATCHER; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SSL_CIPHER_SUITES; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_SSL_PROTOCOLS; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_TRACING; -import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_URI; -import static org.projectnessie.client.NessieConfigConstants.CONF_READ_TIMEOUT; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.Objects; import java.util.function.Function; -import java.util.stream.Collectors; -import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import org.projectnessie.client.NessieClientBuilder; -import org.projectnessie.client.NessieConfigConstants; import org.projectnessie.client.api.NessieApi; import org.projectnessie.client.auth.NessieAuthentication; -import org.projectnessie.client.auth.NessieAuthenticationProvider; -import org.projectnessie.client.http.v1api.HttpApiV1; -import org.projectnessie.client.http.v2api.HttpApiV2; -import org.projectnessie.client.rest.NessieHttpResponseFilter; -import org.projectnessie.model.ser.Views; /** - * A builder class that creates a {@link NessieHttpClient} via {@link HttpClientBuilder#builder()}. + * This is the deprecated builder class to create a {@link NessieApi} instance for + * HTTP/REST. + * + *

Note that this class does not build an HTTP client but a Nessie API client. + * + * @deprecated This class is deprecated for removal. Migrate your code to use {@link + * NessieClientBuilder#createClientBuilder(String, String)} */ -public class HttpClientBuilder implements NessieClientBuilder { +@Deprecated +public class HttpClientBuilder extends NessieHttpClientBuilderImpl { + @Deprecated // for removal public static final String ENABLE_API_COMPATIBILITY_CHECK_SYSTEM_PROPERTY = - "nessie.client.enable-api-compatibility-check"; - - private static final ObjectMapper MAPPER = - new ObjectMapper() - .enable(SerializationFeature.INDENT_OUTPUT) - .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - - private final HttpClient.Builder builder = - HttpClient.builder() - .setObjectMapper(MAPPER) - .addResponseFilter(new NessieHttpResponseFilter()); + CONF_ENABLE_API_COMPATIBILITY_CHECK; - private boolean tracing; - - private boolean enableApiCompatibilityCheck = - Boolean.parseBoolean( - System.getProperty(ENABLE_API_COMPATIBILITY_CHECK_SYSTEM_PROPERTY, "true")); - - protected HttpClientBuilder() {} + public HttpClientBuilder() {} + /** + * Migrate calling code to use {@link + * NessieClientBuilder#createClientBuilderFromSystemSettings()}. + */ + @Deprecated public static HttpClientBuilder builder() { return new HttpClientBuilder(); } - /** - * Same semantics as {@link #fromConfig(Function)}, uses the system properties. - * - * @return {@code this} - * @see #fromConfig(Function) - */ + @Override + public HttpClientBuilder withUri(String uri) { + return (HttpClientBuilder) super.withUri(uri); + } + @Override public HttpClientBuilder fromSystemProperties() { - return fromConfig(System::getProperty); + return (HttpClientBuilder) super.fromSystemProperties(); } - /** - * Configure this HttpClientBuilder instance using a configuration object and standard Nessie - * configuration keys defined by the constants defined in {@link NessieConfigConstants}. - * Non-{@code null} values returned by the {@code configuration}-function will override previously - * configured values. - * - * @param configuration The function that returns a configuration value for a configuration key. - * @return {@code this} - * @see #fromSystemProperties() - */ @Override public HttpClientBuilder fromConfig(Function configuration) { - String uri = configuration.apply(CONF_NESSIE_URI); - if (uri != null) { - withUri(URI.create(uri)); - } - - withAuthenticationFromConfig(configuration); - - String s = configuration.apply(CONF_NESSIE_TRACING); - if (s != null) { - withTracing(Boolean.parseBoolean(s)); - } - s = configuration.apply(CONF_CONNECT_TIMEOUT); - if (s != null) { - withConnectionTimeout(Integer.parseInt(s)); - } - s = configuration.apply(CONF_READ_TIMEOUT); - if (s != null) { - withReadTimeout(Integer.parseInt(s)); - } - s = configuration.apply(CONF_NESSIE_DISABLE_COMPRESSION); - if (s != null) { - withDisableCompression(Boolean.parseBoolean(s)); - } - - SSLParameters sslParameters = new SSLParameters(); - boolean hasSslParameters = false; - s = configuration.apply(CONF_NESSIE_SSL_CIPHER_SUITES); - if (s != null) { - hasSslParameters = true; - sslParameters.setCipherSuites( - Arrays.stream(s.split(",")) - .map(String::trim) - .filter(v -> !v.isEmpty()) - .toArray(String[]::new)); - } - s = configuration.apply(CONF_NESSIE_SSL_PROTOCOLS); - if (s != null) { - hasSslParameters = true; - sslParameters.setProtocols( - Arrays.stream(s.split(",")) - .map(String::trim) - .filter(v -> !v.isEmpty()) - .toArray(String[]::new)); - } - s = configuration.apply(CONF_NESSIE_SNI_HOSTS); - if (s != null) { - hasSslParameters = true; - sslParameters.setServerNames( - Arrays.stream(s.split(",")) - .map(String::trim) - .filter(v -> !v.isEmpty()) - .map(SNIHostName::new) - .collect(Collectors.toList())); - } - s = configuration.apply(CONF_NESSIE_SNI_MATCHER); - if (s != null) { - hasSslParameters = true; - sslParameters.setSNIMatchers(Collections.singletonList(SNIHostName.createSNIMatcher(s))); - } - if (hasSslParameters) { - withSSLParameters(sslParameters); - } - - s = configuration.apply(CONF_NESSIE_HTTP_2); - if (s != null) { - withHttp2Upgrade(Boolean.parseBoolean(s.trim())); - } - - s = configuration.apply(CONF_NESSIE_HTTP_REDIRECT); - if (s != null) { - withFollowRedirects(s.trim()); - } - - s = configuration.apply(CONF_FORCE_URL_CONNECTION_CLIENT); - if (s != null) { - withForceUrlConnectionClient(Boolean.parseBoolean(s.trim())); - } - - s = configuration.apply(CONF_ENABLE_API_COMPATIBILITY_CHECK); - if (s != null) { - withEnableApiCompatibilityCheck(Boolean.parseBoolean(s)); - } - - return this; + return (HttpClientBuilder) super.fromConfig(configuration); } - /** - * Configure only authentication in this HttpClientBuilder instance using a configuration object - * and standard Nessie configuration keys defined by the constants defined in {@link - * NessieConfigConstants}. - * - * @param configuration The function that returns a configuration value for a configuration key. - * @return {@code this} - * @see #fromConfig(Function) - */ @Override - @CanIgnoreReturnValue public HttpClientBuilder withAuthenticationFromConfig(Function configuration) { - withAuthentication(NessieAuthenticationProvider.fromConfig(configuration)); - return this; + return (HttpClientBuilder) super.withAuthenticationFromConfig(configuration); } - /** - * Set the Nessie server URI. A server URI must be configured. - * - * @param uri server URI - * @return {@code this} - */ @Override - @CanIgnoreReturnValue public HttpClientBuilder withUri(URI uri) { - builder.setBaseUri(uri); - return this; + return (HttpClientBuilder) super.withUri(uri); } @Override - @CanIgnoreReturnValue public HttpClientBuilder withAuthentication(NessieAuthentication authentication) { - if (authentication != null && !(authentication instanceof HttpAuthentication)) { - throw new IllegalArgumentException( - "HttpClientBuilder only accepts instances of HttpAuthentication"); - } - builder.setAuthentication((HttpAuthentication) authentication); - return this; + return (HttpClientBuilder) super.withAuthentication(authentication); } - /** - * Whether to enable adding the HTTP headers of an active OpenTracing span to all Nessie requests. - * If enabled, the OpenTracing dependencies must be present at runtime. - * - * @param tracing {@code true} to enable passing HTTP headers for active tracing spans. - * @return {@code this} - */ - @CanIgnoreReturnValue + @Override public HttpClientBuilder withTracing(boolean tracing) { - this.tracing = tracing; - return this; + return (HttpClientBuilder) super.withTracing(tracing); } - /** - * Set the read timeout in milliseconds for this client. Timeout will throw {@link - * HttpClientReadTimeoutException}. - * - * @param readTimeoutMillis number of seconds to wait for a response from server. - * @return {@code this} - */ - @CanIgnoreReturnValue + @Override public HttpClientBuilder withReadTimeout(int readTimeoutMillis) { - builder.setReadTimeoutMillis(readTimeoutMillis); - return this; + return (HttpClientBuilder) super.withReadTimeout(readTimeoutMillis); } - /** - * Set the connection timeout in milliseconds for this client. Timeout will throw {@link - * HttpClientException}. - * - * @param connectionTimeoutMillis number of seconds to wait to connect to the server. - * @return {@code this} - */ - @CanIgnoreReturnValue + @Override public HttpClientBuilder withConnectionTimeout(int connectionTimeoutMillis) { - builder.setConnectionTimeoutMillis(connectionTimeoutMillis); - return this; + return (HttpClientBuilder) super.withConnectionTimeout(connectionTimeoutMillis); } - /** - * Set whether the compression shall be disabled or not. - * - * @param disableCompression whether the compression shall be disabled or not. - * @return {@code this} - */ - @CanIgnoreReturnValue + @Override public HttpClientBuilder withDisableCompression(boolean disableCompression) { - builder.setDisableCompression(disableCompression); - return this; + return (HttpClientBuilder) super.withDisableCompression(disableCompression); } - /** - * Set the SSL context for this client. - * - * @param sslContext the SSL context to use - * @return {@code this} - */ - @CanIgnoreReturnValue + @Override public HttpClientBuilder withSSLContext(SSLContext sslContext) { - builder.setSslContext(sslContext); - return this; + return (HttpClientBuilder) super.withSSLContext(sslContext); } - @CanIgnoreReturnValue + @Override public HttpClientBuilder withSSLParameters(SSLParameters sslParameters) { - builder.setSslParameters(sslParameters); - return this; + return (HttpClientBuilder) super.withSSLParameters(sslParameters); } - @CanIgnoreReturnValue + @Override public HttpClientBuilder withHttp2Upgrade(boolean http2Upgrade) { - builder.setHttp2Upgrade(http2Upgrade); - return this; + return (HttpClientBuilder) super.withHttp2Upgrade(http2Upgrade); } - @CanIgnoreReturnValue + @Override public HttpClientBuilder withFollowRedirects(String redirects) { - builder.setFollowRedirects(redirects); - return this; + return (HttpClientBuilder) super.withFollowRedirects(redirects); } - @CanIgnoreReturnValue + @Override public HttpClientBuilder withForceUrlConnectionClient(boolean forceUrlConnectionClient) { - builder.setForceUrlConnectionClient(forceUrlConnectionClient); - return this; + return (HttpClientBuilder) super.withForceUrlConnectionClient(forceUrlConnectionClient); } - @CanIgnoreReturnValue - public HttpClientBuilder withEnableApiCompatibilityCheck(boolean enable) { - enableApiCompatibilityCheck = enable; - return this; + @Override + public HttpClientBuilder withApiCompatibilityCheck(boolean enable) { + return (HttpClientBuilder) super.withApiCompatibilityCheck(enable); } - @CanIgnoreReturnValue + @Override public HttpClientBuilder withResponseFactory(HttpResponseFactory responseFactory) { - builder.setResponseFactory(responseFactory); - return this; + return (HttpClientBuilder) super.withResponseFactory(responseFactory); } - @SuppressWarnings("unchecked") @Override public API build(Class apiVersion) { - Objects.requireNonNull(apiVersion, "API version class must be non-null"); - - if (tracing) { - // Do this at the last possible moment because once added, tracing cannot be removed. - builder.addTracing(); - } - - if (apiVersion.isAssignableFrom(HttpApiV1.class)) { - if (enableApiCompatibilityCheck) { - builder.addRequestFilter(new NessieApiCompatibilityFilter(builder, 1)); - } - builder.setJsonView(Views.V1.class); - HttpClient httpClient = builder.build(); - return (API) new HttpApiV1(new NessieHttpClient(httpClient)); - } - - if (apiVersion.isAssignableFrom(HttpApiV2.class)) { - if (enableApiCompatibilityCheck) { - builder.addRequestFilter(new NessieApiCompatibilityFilter(builder, 2)); - } - builder.setJsonView(Views.V2.class); - HttpClient httpClient = builder.build(); - return (API) new HttpApiV2(httpClient); - } - - throw new IllegalArgumentException( - String.format("API version %s is not supported.", apiVersion.getName())); + return super.build(apiVersion); } } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilderImpl.java b/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilderImpl.java new file mode 100644 index 00000000000..53643df09f2 --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/http/HttpClientBuilderImpl.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.http; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import org.projectnessie.client.NessieConfigConstants; +import org.projectnessie.client.http.impl.HttpRuntimeConfig; +import org.projectnessie.client.http.impl.HttpUtils; +import org.projectnessie.client.http.impl.jdk11.JavaHttpClient; +import org.projectnessie.client.http.impl.jdk8.UrlConnectionClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class HttpClientBuilderImpl implements HttpClient.Builder { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientBuilderImpl.class); + + private URI baseUri; + private ObjectMapper mapper; + private Class jsonView; + private HttpResponseFactory responseFactory = HttpResponse::new; + private SSLContext sslContext; + private SSLParameters sslParameters; + private HttpAuthentication authentication; + private int readTimeoutMillis = + Integer.getInteger( + "sun.net.client.defaultReadTimeout", NessieConfigConstants.DEFAULT_READ_TIMEOUT_MILLIS); + private int connectionTimeoutMillis = + Integer.getInteger( + "sun.net.client.defaultConnectionTimeout", + NessieConfigConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS); + private boolean disableCompression; + private final List requestFilters = new ArrayList<>(); + private final List responseFilters = new ArrayList<>(); + private boolean http2Upgrade; + private String followRedirects; + private boolean forceUrlConnectionClient; + private int clientSpec = 2; + + HttpClientBuilderImpl() {} + + HttpClientBuilderImpl(HttpClientBuilderImpl other) { + this.baseUri = other.baseUri; + this.mapper = other.mapper; + this.jsonView = other.jsonView; + this.responseFactory = other.responseFactory; + this.sslContext = other.sslContext; + this.sslParameters = other.sslParameters; + this.authentication = other.authentication; + this.readTimeoutMillis = other.readTimeoutMillis; + this.connectionTimeoutMillis = other.connectionTimeoutMillis; + this.disableCompression = other.disableCompression; + this.requestFilters.addAll(other.requestFilters); + this.responseFilters.addAll(other.responseFilters); + this.http2Upgrade = other.http2Upgrade; + this.followRedirects = other.followRedirects; + this.forceUrlConnectionClient = other.forceUrlConnectionClient; + this.clientSpec = other.clientSpec; + } + + /** Creates a (shallow) copy of this builder. */ + @Override + public HttpClient.Builder copy() { + return new HttpClientBuilderImpl(this); + } + + @CanIgnoreReturnValue + public HttpClient.Builder setClientSpec(int clientSpec) { + this.clientSpec = clientSpec; + return this; + } + + @Override + public HttpClient.Builder setBaseUri(URI baseUri) { + this.baseUri = baseUri; + return this; + } + + @Override + public HttpClient.Builder setDisableCompression(boolean disableCompression) { + this.disableCompression = disableCompression; + return this; + } + + @Override + public HttpClient.Builder setObjectMapper(ObjectMapper mapper) { + this.mapper = mapper; + return this; + } + + @Override + public HttpClient.Builder setJsonView(Class jsonView) { + this.jsonView = jsonView; + return this; + } + + @Override + public HttpClient.Builder setResponseFactory(HttpResponseFactory responseFactory) { + this.responseFactory = responseFactory; + return this; + } + + @Override + public HttpClient.Builder setSslContext(SSLContext sslContext) { + this.sslContext = sslContext; + return this; + } + + @Override + public HttpClient.Builder setSslParameters(SSLParameters sslParameters) { + this.sslParameters = sslParameters; + return this; + } + + @Override + public HttpClient.Builder setAuthentication(HttpAuthentication authentication) { + this.authentication = authentication; + return this; + } + + @Override + public HttpClient.Builder setHttp2Upgrade(boolean http2Upgrade) { + this.http2Upgrade = http2Upgrade; + return this; + } + + @Override + public HttpClient.Builder setFollowRedirects(String followRedirects) { + this.followRedirects = followRedirects; + return this; + } + + @Override + public HttpClient.Builder setForceUrlConnectionClient(boolean forceUrlConnectionClient) { + this.forceUrlConnectionClient = forceUrlConnectionClient; + return this; + } + + @Override + public HttpClient.Builder setReadTimeoutMillis(int readTimeoutMillis) { + this.readTimeoutMillis = readTimeoutMillis; + return this; + } + + @Override + public HttpClient.Builder setConnectionTimeoutMillis(int connectionTimeoutMillis) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + return this; + } + + @Override + public HttpClient.Builder addRequestFilter(RequestFilter filter) { + requestFilters.add(filter); + return this; + } + + @Override + public HttpClient.Builder addResponseFilter(ResponseFilter filter) { + responseFilters.add(filter); + return this; + } + + @CanIgnoreReturnValue + public HttpClient.Builder clearRequestFilters() { + requestFilters.clear(); + return this; + } + + @CanIgnoreReturnValue + public HttpClient.Builder clearResponseFilters() { + responseFilters.clear(); + return this; + } + + /** + * Add tracing to the client. This will load the opentracing libraries. It is not possible to + * remove tracing once it is added. + */ + @CanIgnoreReturnValue + public HttpClient.Builder addTracing() { + try { + OpentelemetryTracing.addTracing(this); + } catch (NoClassDefFoundError e) { + LOGGER.warn( + "Failed to initialize tracing, the opentracing libraries are probably missing.", e); + } + return this; + } + + @Override + public HttpClient build() { + HttpUtils.checkArgument( + baseUri != null, "Cannot construct Http client. Must have a non-null uri"); + HttpUtils.checkArgument( + mapper != null, "Cannot construct Http client. Must have a non-null object mapper"); + if (sslContext == null) { + try { + sslContext = SSLContext.getDefault(); + } catch (NoSuchAlgorithmException e) { + throw new HttpClientException( + "Cannot construct Http Client. Default SSL config is invalid.", e); + } + } + + if (authentication != null) { + authentication.applyToHttpClient(this); + } + + HttpRuntimeConfig config = + HttpRuntimeConfig.builder() + .baseUri(baseUri) + .mapper(mapper) + .jsonView(jsonView) + .responseFactory(responseFactory) + .readTimeoutMillis(readTimeoutMillis) + .connectionTimeoutMillis(connectionTimeoutMillis) + .isDisableCompression(disableCompression) + .sslContext(sslContext) + .sslParameters(sslParameters) + .addAllRequestFilters(requestFilters) + .addAllResponseFilters(responseFilters) + .isHttp11Only(!http2Upgrade) + .followRedirects(followRedirects) + .forceUrlConnectionClient(forceUrlConnectionClient) + .build(); + + return ImplSwitch.FACTORY.apply(config); + } + + static class ImplSwitch { + static final Function FACTORY; + + static { + Function factory; + try { + Class.forName("java.net.http.HttpClient"); + factory = + config -> + // Need the system property for tests, "normal" users can use standard + // configuration options. + Boolean.getBoolean("nessie.client.force-url-connection-client") + || config.forceUrlConnectionClient() + ? new UrlConnectionClient(config) + : new JavaHttpClient(config); + } catch (ClassNotFoundException e) { + factory = UrlConnectionClient::new; + } + FACTORY = factory; + } + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpClients.java b/api/client/src/main/java/org/projectnessie/client/http/HttpClients.java new file mode 100644 index 00000000000..740aeb52f12 --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/http/HttpClients.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.http; + +import org.projectnessie.client.rest.NessieHttpResponseFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class HttpClients { + + private static final Logger LOGGER = LoggerFactory.getLogger(HttpClients.class); + + private HttpClients() {} + + static HttpClient buildClient(boolean enableTracing, HttpClient.Builder clientBuilder) { + if (enableTracing) { + addTracing(clientBuilder); + } + clientBuilder.addResponseFilter(new NessieHttpResponseFilter()); + return clientBuilder.build(); + } + + private static void addTracing(HttpClient.Builder httpClient) { + try { + OpentelemetryTracing.addTracing(httpClient); + } catch (NoClassDefFoundError e) { + LOGGER.warn( + "Failed to initialize tracing, the opentracing libraries are probably missing.", e); + } + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClientBuilder.java b/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClientBuilder.java new file mode 100644 index 00000000000..26e5b2c798a --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClientBuilder.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.http; + +import static org.projectnessie.client.NessieConfigConstants.CONF_FORCE_URL_CONNECTION_CLIENT; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_HTTP_2; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_HTTP_REDIRECT; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.function.Function; +import org.projectnessie.client.NessieClientBuilder; +import org.projectnessie.client.NessieConfigConstants; +import org.projectnessie.client.config.NessieClientConfigSources; + +/** HTTP specific Nessie client builder interface. */ +public interface NessieHttpClientBuilder extends NessieClientBuilder { + + /** + * Whether to allow HTTP/2 upgrade, default is {@code false}. + * + *

Only valid on Java 11 and newer with Java's new HTTP client. + */ + @CanIgnoreReturnValue + NessieHttpClientBuilder withHttp2Upgrade(boolean http2Upgrade); + + /** + * Whether HTTP redirects are followed, default is to not follow redirects. + * + *

Valid values are the enum names of {@link java.net.http.HttpClient.Redirect}. + * + *

Only valid on Java 11 and newer with Java's new HTTP client. + */ + @CanIgnoreReturnValue + NessieHttpClientBuilder withFollowRedirects(String redirects); + + /** + * Whether to force using the "old" {@link java.net.URLConnection} based client when running on + * Java 11 and newer with Java's new HTTP client. + */ + @CanIgnoreReturnValue + NessieHttpClientBuilder withForceUrlConnectionClient(boolean forceUrlConnectionClient); + + @CanIgnoreReturnValue + NessieHttpClientBuilder withResponseFactory(HttpResponseFactory responseFactory); + + /** Convenience base class for implementations of {@link NessieHttpClientBuilder}. */ + abstract class AbstractNessieHttpClientBuilder + extends NessieClientBuilder.AbstractNessieClientBuilder implements NessieHttpClientBuilder { + + /** + * Configure this HttpClientBuilder instance using a configuration object and standard Nessie + * configuration keys defined by the constants defined in {@link NessieConfigConstants}. + * Non-{@code null} values returned by the {@code configuration}-function will override + * previously configured values. + * + * @param configuration The function that returns a configuration value for a configuration key. + * @return {@code this} + * @see NessieClientConfigSources + */ + @Override + public NessieHttpClientBuilder fromConfig(Function configuration) { + super.fromConfig(configuration); + + String s = configuration.apply(CONF_NESSIE_HTTP_2); + if (s != null) { + withHttp2Upgrade(Boolean.parseBoolean(s.trim())); + } + + s = configuration.apply(CONF_NESSIE_HTTP_REDIRECT); + if (s != null) { + withFollowRedirects(s.trim()); + } + + s = configuration.apply(CONF_FORCE_URL_CONNECTION_CLIENT); + if (s != null) { + withForceUrlConnectionClient(Boolean.parseBoolean(s.trim())); + } + + return this; + } + + @Override + public NessieHttpClientBuilder withHttp2Upgrade(boolean http2Upgrade) { + return this; + } + + @Override + public NessieHttpClientBuilder withFollowRedirects(String redirects) { + return this; + } + + @Override + public NessieHttpClientBuilder withForceUrlConnectionClient(boolean forceUrlConnectionClient) { + return this; + } + + @Override + public NessieHttpClientBuilder withResponseFactory(HttpResponseFactory responseFactory) { + return this; + } + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClientBuilderImpl.java b/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClientBuilderImpl.java new file mode 100644 index 00000000000..0f0d2986ed4 --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClientBuilderImpl.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2020 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.http; + +import static org.projectnessie.client.NessieConfigConstants.CONF_ENABLE_API_COMPATIBILITY_CHECK; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.net.URI; +import java.util.Objects; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import org.projectnessie.client.NessieClientBuilder; +import org.projectnessie.client.api.NessieApi; +import org.projectnessie.client.auth.NessieAuthentication; +import org.projectnessie.client.rest.NessieHttpResponseFilter; +import org.projectnessie.client.rest.v1.HttpApiV1; +import org.projectnessie.client.rest.v1.RestV1Client; +import org.projectnessie.client.rest.v2.HttpApiV2; +import org.projectnessie.model.ser.Views; + +/** {@link NessieHttpClientBuilder} and {@link NessieClientBuilder} implementation for HTTP/REST. */ +public class NessieHttpClientBuilderImpl + extends NessieHttpClientBuilder.AbstractNessieHttpClientBuilder { + + private static final ObjectMapper MAPPER = + new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + + private final HttpClient.Builder builder = + HttpClient.builder() + .setObjectMapper(MAPPER) + .addResponseFilter(new NessieHttpResponseFilter()); + + private boolean tracing; + + private boolean enableApiCompatibilityCheck = + Boolean.parseBoolean(System.getProperty(CONF_ENABLE_API_COMPATIBILITY_CHECK, "true")); + + public NessieHttpClientBuilderImpl() {} + + @Override + public String name() { + return "HTTP"; + } + + @Override + public int priority() { + return 100; + } + + /** + * Set the Nessie server URI. A server URI must be configured. + * + * @param uri server URI + * @return {@code this} + */ + @Override + @CanIgnoreReturnValue + public NessieHttpClientBuilderImpl withUri(URI uri) { + builder.setBaseUri(uri); + return this; + } + + @Override + @CanIgnoreReturnValue + public NessieHttpClientBuilderImpl withAuthentication(NessieAuthentication authentication) { + if (authentication != null && !(authentication instanceof HttpAuthentication)) { + throw new IllegalArgumentException( + "HttpClientBuilder only accepts instances of HttpAuthentication"); + } + builder.setAuthentication((HttpAuthentication) authentication); + return this; + } + + /** + * Whether to enable adding the HTTP headers of an active OpenTracing span to all Nessie requests. + * If enabled, the OpenTracing dependencies must be present at runtime. + * + * @param tracing {@code true} to enable passing HTTP headers for active tracing spans. + * @return {@code this} + */ + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withTracing(boolean tracing) { + this.tracing = tracing; + return this; + } + + /** + * Set the read timeout in milliseconds for this client. Timeout will throw {@link + * HttpClientReadTimeoutException}. + * + * @param readTimeoutMillis number of seconds to wait for a response from server. + * @return {@code this} + */ + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withReadTimeout(int readTimeoutMillis) { + builder.setReadTimeoutMillis(readTimeoutMillis); + return this; + } + + /** + * Set the connection timeout in milliseconds for this client. Timeout will throw {@link + * HttpClientException}. + * + * @param connectionTimeoutMillis number of seconds to wait to connect to the server. + * @return {@code this} + */ + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withConnectionTimeout(int connectionTimeoutMillis) { + builder.setConnectionTimeoutMillis(connectionTimeoutMillis); + return this; + } + + /** + * Set whether the compression shall be disabled or not. + * + * @param disableCompression whether the compression shall be disabled or not. + * @return {@code this} + */ + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withDisableCompression(boolean disableCompression) { + builder.setDisableCompression(disableCompression); + return this; + } + + /** + * Set the SSL context for this client. + * + * @param sslContext the SSL context to use + * @return {@code this} + */ + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withSSLContext(SSLContext sslContext) { + builder.setSslContext(sslContext); + return this; + } + + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withSSLParameters(SSLParameters sslParameters) { + builder.setSslParameters(sslParameters); + return this; + } + + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withHttp2Upgrade(boolean http2Upgrade) { + builder.setHttp2Upgrade(http2Upgrade); + return this; + } + + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withFollowRedirects(String redirects) { + builder.setFollowRedirects(redirects); + return this; + } + + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withForceUrlConnectionClient( + boolean forceUrlConnectionClient) { + builder.setForceUrlConnectionClient(forceUrlConnectionClient); + return this; + } + + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withApiCompatibilityCheck(boolean enable) { + enableApiCompatibilityCheck = enable; + return this; + } + + @CanIgnoreReturnValue + @Override + public NessieHttpClientBuilderImpl withResponseFactory(HttpResponseFactory responseFactory) { + builder.setResponseFactory(responseFactory); + return this; + } + + @Override + public API build(Class apiVersion) { + Objects.requireNonNull(apiVersion, "API version class must be non-null"); + + if (tracing) { + // Do this at the last possible moment because once added, tracing cannot be removed. + builder.addTracing(); + } + + if (apiVersion.isAssignableFrom(HttpApiV1.class)) { + if (enableApiCompatibilityCheck) { + builder.addRequestFilter(new NessieApiCompatibilityFilter(builder, 1)); + } + builder.setJsonView(Views.V1.class); + HttpClient httpClient = HttpClients.buildClient(tracing, builder); + return apiVersion.cast(new HttpApiV1(new RestV1Client(httpClient))); + } + + if (apiVersion.isAssignableFrom(HttpApiV2.class)) { + if (enableApiCompatibilityCheck) { + builder.addRequestFilter(new NessieApiCompatibilityFilter(builder, 2)); + } + builder.setJsonView(Views.V2.class); + HttpClient httpClient = HttpClients.buildClient(tracing, builder); + return apiVersion.cast(new HttpApiV2(httpClient)); + } + + throw new IllegalArgumentException( + String.format("API version %s is not supported.", apiVersion.getName())); + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/http/impl/jdk11/JavaHttpClient.java b/api/client/src/main/java/org/projectnessie/client/http/impl/jdk11/JavaHttpClient.java index 5a9ec712a54..bf416eea5d9 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/impl/jdk11/JavaHttpClient.java +++ b/api/client/src/main/java/org/projectnessie/client/http/impl/jdk11/JavaHttpClient.java @@ -29,7 +29,7 @@ * *

Java's new {@link HttpClient} provides a lot of benefits, such as HTTP/2 support, proper * redirection support (if enabled), and advanced security capabilities. See the {@link - * org.projectnessie.client.http.HttpClientBuilder} for parameters supported only by this client + * org.projectnessie.client.NessieClientBuilder} for parameters supported only by this client * implementation. */ @SuppressWarnings("Since15") // IntelliJ warns about new APIs. 15 is misleading, it means 11 diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteTag.java b/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteTag.java deleted file mode 100644 index fad040c58fa..00000000000 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteTag.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2022 Dremio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.projectnessie.client.http.v2api; - -import org.projectnessie.client.api.DeleteTagBuilder; -import org.projectnessie.client.builder.BaseOnTagBuilder; -import org.projectnessie.client.http.HttpClient; -import org.projectnessie.error.NessieConflictException; -import org.projectnessie.error.NessieNotFoundException; -import org.projectnessie.model.Reference; -import org.projectnessie.model.SingleReferenceResponse; -import org.projectnessie.model.Tag; - -final class HttpDeleteTag extends BaseOnTagBuilder implements DeleteTagBuilder { - private final HttpClient client; - - HttpDeleteTag(HttpClient client) { - this.client = client; - } - - @Override - public void delete() throws NessieConflictException, NessieNotFoundException { - getAndDelete(); - } - - @Override - public Tag getAndDelete() throws NessieNotFoundException, NessieConflictException { - return (Tag) - client - .newRequest() - .path("trees/{ref}") - .resolveTemplate("ref", Reference.toPathString(tagName, hash)) - .queryParam("type", Reference.ReferenceType.TAG.name()) - .unwrap(NessieConflictException.class, NessieNotFoundException.class) - .delete() - .readEntity(SingleReferenceResponse.class) - .getReference(); - } -} diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpApiV1.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpApiV1.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpApiV1.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpApiV1.java index 041914bda02..e0e37386670 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpApiV1.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpApiV1.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.api.AssignBranchBuilder; import org.projectnessie.client.api.AssignTagBuilder; @@ -36,7 +36,6 @@ import org.projectnessie.client.api.NessieApiV1; import org.projectnessie.client.api.TransplantCommitsBuilder; import org.projectnessie.client.api.UpdateNamespaceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; import org.projectnessie.model.NessieConfiguration; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpAssignBranch.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpAssignBranch.java similarity index 94% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpAssignBranch.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpAssignBranch.java index 46caa1dafd8..1795537716c 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpAssignBranch.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpAssignBranch.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.api.AssignBranchBuilder; import org.projectnessie.client.builder.BaseAssignReferenceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpAssignTag.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpAssignTag.java similarity index 94% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpAssignTag.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpAssignTag.java index 68fb451d560..afe8877d228 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpAssignTag.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpAssignTag.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.api.AssignTagBuilder; import org.projectnessie.client.builder.BaseAssignReferenceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Reference.ReferenceType; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCommitMultipleOperations.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCommitMultipleOperations.java similarity index 93% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCommitMultipleOperations.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCommitMultipleOperations.java index 55dc54fb085..85b9d3ed45d 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCommitMultipleOperations.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCommitMultipleOperations.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.builder.BaseCommitMultipleOperationsBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCreateNamespace.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCreateNamespace.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCreateNamespace.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCreateNamespace.java index 3a055b7ecb5..af22e7944f5 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCreateNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCreateNamespace.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.NamespaceParams; import org.projectnessie.client.api.CreateNamespaceResult; import org.projectnessie.client.builder.BaseCreateNamespaceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNamespaceAlreadyExistsException; import org.projectnessie.error.NessieReferenceNotFoundException; import org.projectnessie.model.CommitMeta; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCreateReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCreateReference.java similarity index 92% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCreateReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCreateReference.java index ddaea1347c1..56a6afab24b 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpCreateReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpCreateReference.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.builder.BaseCreateReferenceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Reference; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteBranch.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteBranch.java similarity index 93% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteBranch.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteBranch.java index 260515db044..49d681821d1 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteBranch.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteBranch.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.api.DeleteBranchBuilder; import org.projectnessie.client.builder.BaseOnBranchBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteNamespace.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteNamespace.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteNamespace.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteNamespace.java index 59200eae27c..722a8e13622 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteNamespace.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.NamespaceParams; import org.projectnessie.api.v1.params.NamespaceParamsBuilder; import org.projectnessie.client.api.DeleteNamespaceResult; import org.projectnessie.client.builder.BaseDeleteNamespaceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNamespaceNotEmptyException; import org.projectnessie.error.NessieNamespaceNotFoundException; import org.projectnessie.error.NessieReferenceNotFoundException; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteTag.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteTag.java similarity index 93% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteTag.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteTag.java index ce0ac0336d6..27f48172881 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpDeleteTag.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpDeleteTag.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.client.api.DeleteTagBuilder; import org.projectnessie.client.builder.BaseOnTagBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Reference; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetAllReferences.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetAllReferences.java similarity index 93% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetAllReferences.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetAllReferences.java index 6a4fcb35184..47102204c7e 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetAllReferences.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetAllReferences.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.ReferencesParams; import org.projectnessie.client.builder.BaseGetAllReferencesBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.model.ReferencesResponse; final class HttpGetAllReferences extends BaseGetAllReferencesBuilder { diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetCommitLog.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetCommitLog.java similarity index 93% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetCommitLog.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetCommitLog.java index 6dc5ef6e662..d4f012fe911 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetCommitLog.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetCommitLog.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.CommitLogParams; import org.projectnessie.client.builder.BaseGetCommitLogBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.LogResponse; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetContent.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetContent.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetContent.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetContent.java index 53060c843fa..5e6a69f5e18 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetContent.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetContent.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import java.util.Map; import java.util.stream.Collectors; import org.projectnessie.client.builder.BaseGetContentBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Content; import org.projectnessie.model.ContentKey; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetDiff.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetDiff.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetDiff.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetDiff.java index 2e33535d6a9..edd2188590b 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetDiff.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetDiff.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import java.util.Collection; import org.projectnessie.api.v1.params.DiffParams; import org.projectnessie.api.v1.params.DiffParamsBuilder; import org.projectnessie.client.api.GetDiffBuilder; import org.projectnessie.client.builder.BaseGetDiffBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.ContentKey; import org.projectnessie.model.DiffResponse; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetEntries.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetEntries.java similarity index 96% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetEntries.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetEntries.java index 9fcba95821f..e9cde3dc28f 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetEntries.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetEntries.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import java.util.Collection; import org.projectnessie.api.v1.params.EntriesParams; import org.projectnessie.client.api.GetEntriesBuilder; import org.projectnessie.client.builder.BaseGetEntriesBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.ContentKey; import org.projectnessie.model.EntriesResponse; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetMultipleNamespaces.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetMultipleNamespaces.java similarity index 94% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetMultipleNamespaces.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetMultipleNamespaces.java index 2b1651c1dd6..4971229559d 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetMultipleNamespaces.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetMultipleNamespaces.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.MultipleNamespacesParams; import org.projectnessie.client.api.GetMultipleNamespacesBuilder; import org.projectnessie.client.builder.BaseGetMultipleNamespacesBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieReferenceNotFoundException; import org.projectnessie.model.GetNamespacesResponse; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetNamespace.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetNamespace.java similarity index 94% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetNamespace.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetNamespace.java index 16c2de4a33c..6bf9d39269d 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetNamespace.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.NamespaceParams; import org.projectnessie.api.v1.params.NamespaceParamsBuilder; import org.projectnessie.client.api.GetNamespaceResult; import org.projectnessie.client.builder.BaseGetNamespaceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNamespaceNotFoundException; import org.projectnessie.error.NessieReferenceNotFoundException; import org.projectnessie.model.Namespace; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetReference.java similarity index 92% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetReference.java index 762d34b88d5..af61ccc1ca8 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpGetReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpGetReference.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.GetReferenceParams; import org.projectnessie.client.builder.BaseGetReferenceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Reference; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpMergeReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpMergeReference.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpMergeReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpMergeReference.java index c10ad5987b9..ffce438e27f 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpMergeReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpMergeReference.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.ImmutableMerge; import org.projectnessie.client.api.MergeReferenceBuilder; import org.projectnessie.client.builder.BaseMergeReferenceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.CommitMeta; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpTransplantCommits.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpTransplantCommits.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpTransplantCommits.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpTransplantCommits.java index 8586b322952..a18833fab3a 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpTransplantCommits.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpTransplantCommits.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.ImmutableTransplant; import org.projectnessie.client.builder.BaseTransplantCommitsBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.MergeResponse; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpUpdateNamespace.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpUpdateNamespace.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v1api/HttpUpdateNamespace.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/HttpUpdateNamespace.java index ddb8b5f95a0..a24ed337aa4 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v1api/HttpUpdateNamespace.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/HttpUpdateNamespace.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.params.ImmutableNamespaceUpdate; import org.projectnessie.api.v1.params.NamespaceParams; import org.projectnessie.api.v1.params.NamespaceParamsBuilder; import org.projectnessie.client.api.UpdateNamespaceResult; import org.projectnessie.client.builder.BaseUpdateNamespaceBuilder; -import org.projectnessie.client.http.NessieApiClient; import org.projectnessie.error.NessieNamespaceNotFoundException; import org.projectnessie.error.NessieReferenceNotFoundException; import org.projectnessie.model.CommitMeta; diff --git a/api/client/src/main/java/org/projectnessie/client/http/NessieApiClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/NessieApiClient.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/NessieApiClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/NessieApiClient.java index bbef95f0bba..e9305de71b5 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/NessieApiClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/NessieApiClient.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import java.io.Closeable; import org.projectnessie.api.v1.http.HttpConfigApi; diff --git a/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1Client.java similarity index 82% rename from api/client/src/main/java/org/projectnessie/client/http/NessieHttpClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1Client.java index 27d1bf3fbec..e15930c3af9 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/NessieHttpClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1Client.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; @@ -24,9 +24,11 @@ import org.projectnessie.api.v1.http.HttpDiffApi; import org.projectnessie.api.v1.http.HttpNamespaceApi; import org.projectnessie.api.v1.http.HttpTreeApi; +import org.projectnessie.client.http.HttpClient; +import org.projectnessie.client.http.HttpClientException; import org.projectnessie.error.BaseNessieClientServerException; -final class NessieHttpClient extends NessieApiClient { +public final class RestV1Client extends NessieApiClient { private final HttpClient httpClient; @@ -34,14 +36,13 @@ final class NessieHttpClient extends NessieApiClient { * Create new HTTP Nessie client. All REST api endpoints are mapped here. This client should * support any JAX-RS implementation */ - @SuppressWarnings("deprecation") - NessieHttpClient(HttpClient client) { + public RestV1Client(HttpClient client) { super( - wrap(HttpConfigApi.class, new HttpConfigClient(client)), - wrap(HttpTreeApi.class, new HttpTreeClient(client)), - wrap(HttpContentApi.class, new HttpContentClient(client)), - wrap(HttpDiffApi.class, new HttpDiffClient(client)), - wrap(HttpNamespaceApi.class, new HttpNamespaceClient(client))); + wrap(HttpConfigApi.class, new RestV1ConfigClient(client)), + wrap(HttpTreeApi.class, new RestV1TreeClient(client)), + wrap(HttpContentApi.class, new RestV1ContentClient(client)), + wrap(HttpDiffApi.class, new RestV1DiffClient(client)), + wrap(HttpNamespaceApi.class, new RestV1NamespaceClient(client))); this.httpClient = client; } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpConfigClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1ConfigClient.java similarity index 83% rename from api/client/src/main/java/org/projectnessie/client/http/HttpConfigClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1ConfigClient.java index 5b81f9427e8..8f22de03817 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpConfigClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1ConfigClient.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import org.projectnessie.api.v1.http.HttpConfigApi; +import org.projectnessie.client.http.HttpClient; import org.projectnessie.model.NessieConfiguration; -class HttpConfigClient implements HttpConfigApi { +class RestV1ConfigClient implements HttpConfigApi { private final HttpClient client; - HttpConfigClient(HttpClient client) { + RestV1ConfigClient(HttpClient client) { this.client = client; } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpContentClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1ContentClient.java similarity index 91% rename from api/client/src/main/java/org/projectnessie/client/http/HttpContentClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1ContentClient.java index c35d7705345..0ed1d806bb8 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpContentClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1ContentClient.java @@ -13,21 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import javax.validation.constraints.NotNull; import org.projectnessie.api.v1.http.HttpContentApi; +import org.projectnessie.client.http.HttpClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Content; import org.projectnessie.model.ContentKey; import org.projectnessie.model.GetMultipleContentsRequest; import org.projectnessie.model.GetMultipleContentsResponse; -class HttpContentClient implements HttpContentApi { +class RestV1ContentClient implements HttpContentApi { private final HttpClient client; - public HttpContentClient(HttpClient client) { + RestV1ContentClient(HttpClient client) { this.client = client; } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpDiffClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1DiffClient.java similarity index 89% rename from api/client/src/main/java/org/projectnessie/client/http/HttpDiffClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1DiffClient.java index 794cfc2c9f8..14cd1468e5f 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpDiffClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1DiffClient.java @@ -13,19 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import javax.validation.constraints.NotNull; import org.projectnessie.api.v1.http.HttpDiffApi; import org.projectnessie.api.v1.params.DiffParams; +import org.projectnessie.client.http.HttpClient; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.DiffResponse; -class HttpDiffClient implements HttpDiffApi { +class RestV1DiffClient implements HttpDiffApi { private final HttpClient client; - public HttpDiffClient(HttpClient client) { + RestV1DiffClient(HttpClient client) { this.client = client; } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpNamespaceClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1NamespaceClient.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/HttpNamespaceClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1NamespaceClient.java index d42e2449ba4..72808c02949 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpNamespaceClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1NamespaceClient.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import javax.validation.constraints.NotNull; import org.projectnessie.api.v1.http.HttpNamespaceApi; import org.projectnessie.api.v1.params.MultipleNamespacesParams; import org.projectnessie.api.v1.params.NamespaceParams; import org.projectnessie.api.v1.params.NamespaceUpdate; +import org.projectnessie.client.http.HttpClient; import org.projectnessie.error.NessieNamespaceAlreadyExistsException; import org.projectnessie.error.NessieNamespaceNotEmptyException; import org.projectnessie.error.NessieNamespaceNotFoundException; @@ -27,11 +28,11 @@ import org.projectnessie.model.GetNamespacesResponse; import org.projectnessie.model.Namespace; -class HttpNamespaceClient implements HttpNamespaceApi { +class RestV1NamespaceClient implements HttpNamespaceApi { private final HttpClient client; - public HttpNamespaceClient(HttpClient client) { + RestV1NamespaceClient(HttpClient client) { this.client = client; } diff --git a/api/client/src/main/java/org/projectnessie/client/http/HttpTreeClient.java b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1TreeClient.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/HttpTreeClient.java rename to api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1TreeClient.java index 3c44dc3dd65..62832cbb826 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/HttpTreeClient.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v1/RestV1TreeClient.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import java.util.Locale; import javax.validation.Valid; @@ -25,6 +25,8 @@ import org.projectnessie.api.v1.params.Merge; import org.projectnessie.api.v1.params.ReferencesParams; import org.projectnessie.api.v1.params.Transplant; +import org.projectnessie.client.http.HttpClient; +import org.projectnessie.client.http.HttpRequest; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; @@ -36,11 +38,11 @@ import org.projectnessie.model.Reference; import org.projectnessie.model.ReferencesResponse; -class HttpTreeClient implements HttpTreeApi { +class RestV1TreeClient implements HttpTreeApi { private final HttpClient client; - public HttpTreeClient(HttpClient client) { + RestV1TreeClient(HttpClient client) { this.client = client; } diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/BaseHttpAssignReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/BaseHttpAssignReference.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/BaseHttpAssignReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/BaseHttpAssignReference.java index 4d028a1391f..6ea5626a7a9 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/BaseHttpAssignReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/BaseHttpAssignReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.builder.BaseAssignReferenceBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/BaseHttpDeleteReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/BaseHttpDeleteReference.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/BaseHttpDeleteReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/BaseHttpDeleteReference.java index 4422d823c9d..1ae531a0d49 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/BaseHttpDeleteReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/BaseHttpDeleteReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.builder.BaseChangeReferenceBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpApiV2.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpApiV2.java similarity index 75% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpApiV2.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpApiV2.java index 73e8a42f7f2..dd967ae31a7 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpApiV2.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpApiV2.java @@ -13,16 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.AssignBranchBuilder; import org.projectnessie.client.api.AssignReferenceBuilder; import org.projectnessie.client.api.AssignTagBuilder; import org.projectnessie.client.api.CommitMultipleOperationsBuilder; -import org.projectnessie.client.api.CreateNamespaceBuilder; import org.projectnessie.client.api.CreateReferenceBuilder; import org.projectnessie.client.api.DeleteBranchBuilder; -import org.projectnessie.client.api.DeleteNamespaceBuilder; import org.projectnessie.client.api.DeleteReferenceBuilder; import org.projectnessie.client.api.DeleteTagBuilder; import org.projectnessie.client.api.GetAllReferencesBuilder; @@ -30,22 +28,13 @@ import org.projectnessie.client.api.GetContentBuilder; import org.projectnessie.client.api.GetDiffBuilder; import org.projectnessie.client.api.GetEntriesBuilder; -import org.projectnessie.client.api.GetMultipleNamespacesBuilder; -import org.projectnessie.client.api.GetNamespaceBuilder; -import org.projectnessie.client.api.GetRefLogBuilder; import org.projectnessie.client.api.GetReferenceBuilder; import org.projectnessie.client.api.GetRepositoryConfigBuilder; import org.projectnessie.client.api.MergeReferenceBuilder; import org.projectnessie.client.api.NessieApiV2; import org.projectnessie.client.api.TransplantCommitsBuilder; -import org.projectnessie.client.api.UpdateNamespaceBuilder; import org.projectnessie.client.api.UpdateRepositoryConfigBuilder; import org.projectnessie.client.http.HttpClient; -import org.projectnessie.client.util.v2api.ClientSideCreateNamespace; -import org.projectnessie.client.util.v2api.ClientSideDeleteNamespace; -import org.projectnessie.client.util.v2api.ClientSideGetMultipleNamespaces; -import org.projectnessie.client.util.v2api.ClientSideGetNamespace; -import org.projectnessie.client.util.v2api.ClientSideUpdateNamespace; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; import org.projectnessie.model.NessieConfiguration; @@ -160,37 +149,6 @@ public GetDiffBuilder getDiff() { return new HttpGetDiff(client); } - @Override - @Deprecated - public GetRefLogBuilder getRefLog() { - throw new UnsupportedOperationException("Reflog is not supported in API v2"); - } - - @Override - public GetNamespaceBuilder getNamespace() { - return new ClientSideGetNamespace(this); - } - - @Override - public GetMultipleNamespacesBuilder getMultipleNamespaces() { - return new ClientSideGetMultipleNamespaces(this); - } - - @Override - public CreateNamespaceBuilder createNamespace() { - return new ClientSideCreateNamespace(this); - } - - @Override - public DeleteNamespaceBuilder deleteNamespace() { - return new ClientSideDeleteNamespace(this); - } - - @Override - public UpdateNamespaceBuilder updateProperties() { - return new ClientSideUpdateNamespace(this); - } - @Override public GetRepositoryConfigBuilder getRepositoryConfig() { return new HttpGetRepositoryConfig(client); diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignBranch.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignBranch.java similarity index 96% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignBranch.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignBranch.java index 6ddccec4f77..a7bebb6fd13 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignBranch.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignBranch.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.AssignBranchBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignReference.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignReference.java index 1d8f649ca93..cdf2c4cf1a5 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.AssignReferenceBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignTag.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignTag.java similarity index 96% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignTag.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignTag.java index ed53477dfeb..16c48b54891 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpAssignTag.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpAssignTag.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.AssignTagBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpCommitMultipleOperations.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpCommitMultipleOperations.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpCommitMultipleOperations.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpCommitMultipleOperations.java index b62d6292ff5..6e45aee8bc6 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpCommitMultipleOperations.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpCommitMultipleOperations.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.builder.BaseCommitMultipleOperationsBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpCreateReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpCreateReference.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpCreateReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpCreateReference.java index 616d33ee326..63b024ba33f 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpCreateReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpCreateReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.builder.BaseCreateReferenceBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteBranch.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteBranch.java similarity index 96% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteBranch.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteBranch.java index ad24ec73bfa..1a760b9ee00 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteBranch.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteBranch.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.DeleteBranchBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteReference.java similarity index 95% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteReference.java index c052433255b..d22f1c3ee9d 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpDeleteReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.DeleteReferenceBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteTag.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteTag.java new file mode 100644 index 00000000000..0d92f9ef8b3 --- /dev/null +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpDeleteTag.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.rest.v2; + +import org.projectnessie.client.api.DeleteTagBuilder; +import org.projectnessie.client.http.HttpClient; +import org.projectnessie.model.Reference; +import org.projectnessie.model.Tag; + +final class HttpDeleteTag extends BaseHttpDeleteReference + implements DeleteTagBuilder { + HttpDeleteTag(HttpClient client) { + super(client); + refType(Reference.ReferenceType.TAG); + } + + @Override + public DeleteTagBuilder tagName(String tagName) { + return refName(tagName); + } +} diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetAllReferences.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetAllReferences.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetAllReferences.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetAllReferences.java index e3bb84502c1..d120eb0eb83 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetAllReferences.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetAllReferences.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.api.v2.params.ReferencesParams; import org.projectnessie.client.builder.BaseGetAllReferencesBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetCommitLog.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetCommitLog.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetCommitLog.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetCommitLog.java index 607299fff6b..655cb84f4f7 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetCommitLog.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetCommitLog.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.api.v2.params.CommitLogParams; import org.projectnessie.client.builder.BaseGetCommitLogBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetContent.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetContent.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetContent.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetContent.java index bca3bb9617a..364206a6521 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetContent.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetContent.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import java.util.Map; import org.projectnessie.client.builder.BaseGetContentBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetDiff.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetDiff.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetDiff.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetDiff.java index 87bec1025a0..d7884dff0dd 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetDiff.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetDiff.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.api.v2.params.DiffParams; import org.projectnessie.client.builder.BaseGetDiffBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetEntries.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetEntries.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetEntries.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetEntries.java index 1ef1484cec6..1ba43e90059 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetEntries.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetEntries.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.api.v2.params.EntriesParams; import org.projectnessie.client.builder.BaseGetEntriesBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetReference.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetReference.java index 2b68a511bf7..9013e53c2e2 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.builder.BaseGetReferenceBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetRepositoryConfig.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetRepositoryConfig.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetRepositoryConfig.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetRepositoryConfig.java index 1bbbddcb116..da099a56ced 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpGetRepositoryConfig.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpGetRepositoryConfig.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import static java.util.Objects.requireNonNull; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpMergeReference.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpMergeReference.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpMergeReference.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpMergeReference.java index e83623a9e16..8a0a3de439c 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpMergeReference.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpMergeReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.api.v2.params.ImmutableMerge; import org.projectnessie.client.api.MergeReferenceBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpTransplantCommits.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpTransplantCommits.java similarity index 98% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpTransplantCommits.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpTransplantCommits.java index 63d0869c04f..490bfc8fe7b 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpTransplantCommits.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpTransplantCommits.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.api.v2.params.ImmutableTransplant; import org.projectnessie.client.api.TransplantCommitsBuilder; diff --git a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpUpdateRepositoryConfig.java b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpUpdateRepositoryConfig.java similarity index 97% rename from api/client/src/main/java/org/projectnessie/client/http/v2api/HttpUpdateRepositoryConfig.java rename to api/client/src/main/java/org/projectnessie/client/rest/v2/HttpUpdateRepositoryConfig.java index 4898a7253c4..637859a8494 100644 --- a/api/client/src/main/java/org/projectnessie/client/http/v2api/HttpUpdateRepositoryConfig.java +++ b/api/client/src/main/java/org/projectnessie/client/rest/v2/HttpUpdateRepositoryConfig.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v2api; +package org.projectnessie.client.rest.v2; import org.projectnessie.client.api.UpdateRepositoryConfigBuilder; import org.projectnessie.client.http.HttpClient; diff --git a/api/client/src/main/resources/META-INF/services/org.projectnessie.client.NessieClientBuilder b/api/client/src/main/resources/META-INF/services/org.projectnessie.client.NessieClientBuilder new file mode 100644 index 00000000000..5d57887bbe5 --- /dev/null +++ b/api/client/src/main/resources/META-INF/services/org.projectnessie.client.NessieClientBuilder @@ -0,0 +1,16 @@ +# +# Copyright (C) 2023 Dremio +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +org.projectnessie.client.http.NessieHttpClientBuilderImpl diff --git a/api/client/src/test/java/org/projectnessie/client/builder/TestResultStreamPaginator.java b/api/client/src/test/java/org/projectnessie/client/builder/TestResultStreamPaginator.java index ae06532b8e8..75ce8cc8bf6 100644 --- a/api/client/src/test/java/org/projectnessie/client/builder/TestResultStreamPaginator.java +++ b/api/client/src/test/java/org/projectnessie/client/builder/TestResultStreamPaginator.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; import org.junit.jupiter.api.Test; +import org.projectnessie.client.builder.StreamingUtil.ResultStreamPaginator; import org.projectnessie.error.NessieReferenceNotFoundException; import org.projectnessie.model.PaginatedResponse; diff --git a/api/client/src/test/java/org/projectnessie/client/config/TestNessieClientConfigSources.java b/api/client/src/test/java/org/projectnessie/client/config/TestNessieClientConfigSources.java new file mode 100644 index 00000000000..1bdb8098b67 --- /dev/null +++ b/api/client/src/test/java/org/projectnessie/client/config/TestNessieClientConfigSources.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.client.config; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static org.projectnessie.client.config.NessieClientConfigSources.dotEnvFile; +import static org.projectnessie.client.config.NessieClientConfigSources.environmentConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.environmentFileConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.mapConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.nessieClientConfigFile; +import static org.projectnessie.client.config.NessieClientConfigSources.propertiesConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.propertiesFileConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.systemEnvironmentConfigSource; +import static org.projectnessie.client.config.NessieClientConfigSources.systemPropertiesConfigSource; + +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; + +@ExtendWith(SoftAssertionsExtension.class) +public class TestNessieClientConfigSources { + @InjectSoftAssertions protected SoftAssertions soft; + + @Test + void mapSource() { + soft.assertThat(asMap(mapConfigSource(emptyMap()), "a", "b")) + .containsEntry("a", null) + .containsEntry("b", null); + soft.assertThat(asMap(mapConfigSource(singletonMap("a", "foo")), "a", "b")) + .containsEntry("a", "foo") + .containsEntry("b", null); + soft.assertThat( + asMap( + mapConfigSource(ImmutableMap.of("a", "foo", "b", "bar", "c", "baz")), + "a", + "b", + "c")) + .containsEntry("a", "foo") + .containsEntry("b", "bar") + .containsEntry("c", "baz"); + } + + @Test + void propertiesSource() { + soft.assertThat(asMap(propertiesConfigSource(new Properties()), "a", "b")) + .containsEntry("a", null) + .containsEntry("b", null); + + Properties p = new Properties(); + p.put("a", "foo"); + soft.assertThat(asMap(propertiesConfigSource(p), "a", "b")) + .containsEntry("a", "foo") + .containsEntry("b", null); + + p = new Properties(); + p.put("a", "foo"); + p.put("b", "bar"); + p.put("c", "baz"); + soft.assertThat(asMap(propertiesConfigSource(p), "a", "b", "c")) + .containsEntry("a", "foo") + .containsEntry("b", "bar") + .containsEntry("c", "baz"); + } + + @Test + void environmentSource() { + soft.assertThat(asMap(environmentConfigSource(emptyMap()), "a.b", "b.c")) + .containsEntry("a.b", null) + .containsEntry("b.c", null); + soft.assertThat(asMap(environmentConfigSource(singletonMap("A_B", "foo")), "a.b", "b.c")) + .containsEntry("a.b", "foo") + .containsEntry("b.c", null); + soft.assertThat( + asMap( + environmentConfigSource( + ImmutableMap.of("A_B", "foo", "B_C", "bar", "C_D_MINUS", "baz")), + "a.b", + "b.c", + "c.d-minus")) + .containsEntry("a.b", "foo") + .containsEntry("b.c", "bar") + .containsEntry("c.d-minus", "baz"); + } + + @Test + void environmentFileSource(@TempDir Path dir) throws IOException { + Path f1 = dir.resolve("c_1"); + Path f2 = dir.resolve("c_2"); + Path f3 = dir.resolve("c_3"); + Files.createFile(f1); + Files.write(f2, singletonList("A_B=foo")); + Files.write(f3, asList("A_B=foo", "B_C=bar", "C_D_MINUS=baz", "D_E=0")); + + soft.assertThat(asMap(environmentFileConfigSource(f1), "a.b", "b.c")) + .containsEntry("a.b", null) + .containsEntry("b.c", null); + soft.assertThat(asMap(environmentFileConfigSource(f2), "a.b", "b.c")) + .containsEntry("a.b", "foo") + .containsEntry("b.c", null); + soft.assertThat(asMap(environmentFileConfigSource(f3), "a.b", "b.c", "c.d-minus", "d.e")) + .containsEntry("a.b", "foo") + .containsEntry("b.c", "bar") + .containsEntry("c.d-minus", "baz") + .containsEntry("d.e", "0"); + } + + @Test + void propertiesFileSource(@TempDir Path dir) throws IOException { + Path f1 = dir.resolve("c_1"); + Path f2 = dir.resolve("c_2"); + Path f3 = dir.resolve("c_3"); + Files.createFile(f1); + Files.write(f2, singletonList("a.b=foo")); + Files.write(f3, asList("a.b=foo", "b.c=bar", "c.d-minus=baz", "d.e=0")); + + soft.assertThat(asMap(propertiesFileConfigSource(f1), "a.b", "b.c")) + .containsEntry("a.b", null) + .containsEntry("b.c", null); + soft.assertThat(asMap(propertiesFileConfigSource(f2), "a.b", "b.c")) + .containsEntry("a.b", "foo") + .containsEntry("b.c", null); + soft.assertThat(asMap(propertiesFileConfigSource(f3), "a.b", "b.c", "c.d-minus", "d.e")) + .containsEntry("a.b", "foo") + .containsEntry("b.c", "bar") + .containsEntry("c.d-minus", "baz") + .containsEntry("d.e", "0"); + } + + @Test + void systemEnvironmentSource() { + NessieClientConfigSource configSource = systemEnvironmentConfigSource(); + + Map env = System.getenv(); + for (Map.Entry e : env.entrySet()) { + if (e.getKey().chars().anyMatch(c -> !(Character.isUpperCase(c) || c == '_'))) { + // ignore env-vars with lower-case chars and chars not a '-' + continue; + } + + String k = e.getKey().replace('_', '.').toLowerCase(Locale.ROOT); + soft.assertThat(configSource.getValue(k)) + .describedAs("Env var %s, using %s", e.getKey(), k) + .isEqualTo(e.getValue()); + } + } + + @Test + void systemPropertiesSource() { + NessieClientConfigSource configSource = systemPropertiesConfigSource(); + + for (Map.Entry e : System.getProperties().entrySet()) { + soft.assertThat(configSource.getValue(e.getKey().toString())) + .describedAs("System property %s", e.getKey()) + .isEqualTo(e.getValue()); + } + } + + @Test + void dotEnvFilePath() { + soft.assertThat(dotEnvFile().toAbsolutePath()) + .isEqualTo(Paths.get(System.getProperty("user.dir"), ".env").toAbsolutePath()); + } + + @Test + void clientConfigFilePath() { + soft.assertThat(nessieClientConfigFile().toAbsolutePath()) + .isEqualTo( + Paths.get(System.getProperty("user.dir"), ".config/nessie/nessie-client.properties") + .toAbsolutePath()); + } + + static Map asMap(NessieClientConfigSource configSource, String... keys) { + Map r = new HashMap<>(); + for (String key : keys) { + r.put(key, configSource.getValue(key)); + } + return r; + } +} diff --git a/api/client/src/test/java/org/projectnessie/client/http/TestHttpClientBuilder.java b/api/client/src/test/java/org/projectnessie/client/http/TestHttpClientBuilder.java index c8278360662..f65ca8ff7a8 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/TestHttpClientBuilder.java +++ b/api/client/src/test/java/org/projectnessie/client/http/TestHttpClientBuilder.java @@ -19,7 +19,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.projectnessie.client.http.HttpClientBuilder.ENABLE_API_COMPATIBILITY_CHECK_SYSTEM_PROPERTY; +import static org.projectnessie.client.NessieClientBuilder.createClientBuilderFromSystemSettings; +import static org.projectnessie.client.NessieConfigConstants.CONF_ENABLE_API_COMPATIBILITY_CHECK; import java.net.URI; import java.util.Arrays; @@ -30,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.projectnessie.client.NessieClientBuilder; import org.projectnessie.client.NessieConfigConstants; import org.projectnessie.client.api.NessieApi; import org.projectnessie.client.api.NessieApiV1; @@ -47,7 +49,7 @@ interface IncompatibleApiInterface extends NessieApi {} void testIncompatibleApiInterface() { assertThatThrownBy( () -> - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(URI.create("http://localhost")) .build(IncompatibleApiInterface.class)) .isInstanceOf(IllegalArgumentException.class) @@ -59,7 +61,7 @@ void testIncompatibleApiInterface() { void testIncompatibleAuthProvider() { assertThatThrownBy( () -> - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(URI.create("http://localhost")) .withAuthentication(new NessieAuthentication() {}) .build(IncompatibleApiInterface.class)) @@ -70,14 +72,17 @@ void testIncompatibleAuthProvider() { @Test void testNullUri() { assertThatThrownBy( - () -> HttpClientBuilder.builder().withUri((URI) null).build(NessieApiV1.class)) + () -> + createClientBuilderFromSystemSettings() + .withUri((URI) null) + .build(NessieApiV1.class)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Cannot construct Http client. Must have a non-null uri"); } @Test void testNoUri() { - assertThatThrownBy(() -> HttpClientBuilder.builder().build(NessieApiV1.class)) + assertThatThrownBy(() -> createClientBuilderFromSystemSettings().build(NessieApiV1.class)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Cannot construct Http client. Must have a non-null uri"); } @@ -86,7 +91,7 @@ void testNoUri() { void testInvalidUriScheme() { assertThatThrownBy( () -> - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(URI.create("file:///foo/bar/baz")) .build(NessieApiV1.class)) .isInstanceOf(IllegalArgumentException.class) @@ -95,7 +100,7 @@ void testInvalidUriScheme() { } @SuppressWarnings("deprecation") - static List> basicAuthConfigs() { + static List> basicAuthConfigs() { return Arrays.asList( cfg -> cfg.withAuthentication( @@ -118,14 +123,14 @@ static List> basicAuthConfigs() { @ParameterizedTest @MethodSource("basicAuthConfigs") - void testAuthBasic(Function config) throws Exception { + void testAuthBasic(Function config) throws Exception { AtomicReference authHeader = new AtomicReference<>(); try (HttpTestServer server = new HttpTestServer(handlerForHeaderTest("Authorization", authHeader))) { try (NessieApiV1 client = config - .apply(HttpClientBuilder.builder().withUri(server.getUri())) + .apply(createClientBuilderFromSystemSettings().withUri(server.getUri())) .build(NessieApiV1.class)) { client.getConfig(); } @@ -144,25 +149,25 @@ void testAuthBasic(Function config) throws void testApiCompatibilityCheckDisabled() { assertThatCode( () -> - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(URI.create("http://non-existent-host")) - .withEnableApiCompatibilityCheck(false) + .withApiCompatibilityCheck(false) .build(NessieApiV2.class)) .doesNotThrowAnyException(); } @Test void testApiCompatibilityCheckDisabledWithSystemProperties() { - System.setProperty(ENABLE_API_COMPATIBILITY_CHECK_SYSTEM_PROPERTY, "false"); + System.setProperty(CONF_ENABLE_API_COMPATIBILITY_CHECK, "false"); try { assertThatCode( () -> - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(URI.create("http://non-existent-host")) .build(NessieApiV2.class)) .doesNotThrowAnyException(); } finally { - System.clearProperty(ENABLE_API_COMPATIBILITY_CHECK_SYSTEM_PROPERTY); + System.clearProperty(CONF_ENABLE_API_COMPATIBILITY_CHECK); } } diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpAssignBranch.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpAssignBranch.java similarity index 95% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpAssignBranch.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpAssignBranch.java index b3b1d445006..15136bc88b2 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpAssignBranch.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpAssignBranch.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpAssignTag.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpAssignTag.java similarity index 95% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpAssignTag.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpAssignTag.java index 45d00c469ad..e7a23df2d2f 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpAssignTag.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpAssignTag.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpCommitMultipleOperations.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpCommitMultipleOperations.java similarity index 95% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpCommitMultipleOperations.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpCommitMultipleOperations.java index b94c560ae76..c0b0b63e8b5 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpCommitMultipleOperations.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpCommitMultipleOperations.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpDeleteBranch.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpDeleteBranch.java similarity index 95% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpDeleteBranch.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpDeleteBranch.java index dc502550fd3..a1a091ced6f 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpDeleteBranch.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpDeleteBranch.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpDeleteTag.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpDeleteTag.java similarity index 95% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpDeleteTag.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpDeleteTag.java index e1d2e5daa07..9f7e19aa796 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpDeleteTag.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpDeleteTag.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpGetDiff.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpGetDiff.java similarity index 96% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpGetDiff.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpGetDiff.java index 7eda268975a..76857c84c4c 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpGetDiff.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpGetDiff.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpMergeReference.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpMergeReference.java similarity index 95% rename from api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpMergeReference.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpMergeReference.java index f79d505ff30..df404428442 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/v1api/TestHttpMergeReference.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestHttpMergeReference.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http.v1api; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/api/client/src/test/java/org/projectnessie/client/http/TestNessieHttpClient.java b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestRestV1Client.java similarity index 90% rename from api/client/src/test/java/org/projectnessie/client/http/TestNessieHttpClient.java rename to api/client/src/test/java/org/projectnessie/client/rest/v1/TestRestV1Client.java index 83d78305dee..4098f3189bc 100644 --- a/api/client/src/test/java/org/projectnessie/client/http/TestNessieHttpClient.java +++ b/api/client/src/test/java/org/projectnessie/client/rest/v1/TestRestV1Client.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.projectnessie.client.http; +package org.projectnessie.client.rest.v1; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.projectnessie.client.NessieClientBuilder.createClientBuilderFromSystemSettings; import com.fasterxml.jackson.databind.ObjectMapper; import io.opentelemetry.api.GlobalOpenTelemetry; @@ -38,18 +39,19 @@ import org.junit.jupiter.params.provider.ValueSource; import org.projectnessie.client.api.NessieApiV1; import org.projectnessie.client.api.NessieApiV2; -import org.projectnessie.client.http.v1api.HttpApiV1; -import org.projectnessie.client.http.v2api.HttpApiV2; +import org.projectnessie.client.http.HttpClient; +import org.projectnessie.client.http.NessieApiCompatibilityException; import org.projectnessie.client.rest.NessieBadResponseException; import org.projectnessie.client.rest.NessieInternalServerException; import org.projectnessie.client.rest.NessieNotAuthorizedException; +import org.projectnessie.client.rest.v2.HttpApiV2; import org.projectnessie.client.util.HttpTestServer; import org.projectnessie.client.util.HttpTestUtil; import org.projectnessie.client.util.JaegerTestTracer; import org.projectnessie.model.Branch; @ExtendWith(SoftAssertionsExtension.class) -class TestNessieHttpClient { +class TestRestV1Client { @InjectSoftAssertions SoftAssertions soft; @@ -69,10 +71,10 @@ void testNonJsonResponse() throws Exception { }; try (HttpTestServer server = new HttpTestServer(handler); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri()) .withTracing(true) - .withEnableApiCompatibilityCheck(false) + .withApiCompatibilityCheck(false) .build(NessieApiV1.class)) { assertThatThrownBy(api::getDefaultBranch) .isInstanceOf(NessieBadResponseException.class) @@ -99,10 +101,10 @@ void testValidJsonResponse(String contentType) throws Exception { }; try (HttpTestServer server = new HttpTestServer("/trees/tree", handler); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri()) .withTracing(true) - .withEnableApiCompatibilityCheck(false) + .withApiCompatibilityCheck(false) .build(NessieApiV1.class)) { api.getDefaultBranch(); } @@ -115,7 +117,7 @@ void testTracing() throws Exception { try (HttpTestServer server = new HttpTestServer(handlerForHeaderTest(W3C_PROPAGATION_HEADER_NAME, traceId)); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri()) .withTracing(true) .build(NessieApiV1.class)) { @@ -139,7 +141,7 @@ void testTracingNotEnabled() throws Exception { try (HttpTestServer server = new HttpTestServer(handlerForHeaderTest(W3C_PROPAGATION_HEADER_NAME, traceId)); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri()) .withTracing(false) .build(NessieApiV1.class)) { @@ -165,9 +167,9 @@ private HttpTestServer errorServer(int status) throws Exception { void testNotFoundOnBaseUri() throws Exception { try (HttpTestServer server = errorServer(404); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri().resolve("/unknownPath")) - .withEnableApiCompatibilityCheck(false) + .withApiCompatibilityCheck(false) .build(NessieApiV1.class)) { assertThatThrownBy(api::getConfig) .isInstanceOf(RuntimeException.class) @@ -179,9 +181,9 @@ void testNotFoundOnBaseUri() throws Exception { void testInternalServerError() throws Exception { try (HttpTestServer server = errorServer(500); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri().resolve("/broken")) - .withEnableApiCompatibilityCheck(false) + .withApiCompatibilityCheck(false) .build(NessieApiV1.class)) { assertThatThrownBy(api::getConfig) .isInstanceOf(NessieInternalServerException.class) @@ -193,9 +195,9 @@ void testInternalServerError() throws Exception { void testUnauthorized() throws Exception { try (HttpTestServer server = errorServer(401); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri().resolve("/unauthorized")) - .withEnableApiCompatibilityCheck(false) + .withApiCompatibilityCheck(false) .build(NessieApiV1.class)) { assertThatThrownBy(api::getConfig) .isInstanceOf(NessieNotAuthorizedException.class) @@ -249,9 +251,9 @@ private void testConfig( throws Exception { try (HttpTestServer server = forConfig(min, max, actual); NessieApiV1 api = - HttpClientBuilder.builder() + createClientBuilderFromSystemSettings() .withUri(server.getUri()) - .withEnableApiCompatibilityCheck(check) + .withApiCompatibilityCheck(check) .build(apiClass)) { api.getConfig(); } @@ -276,7 +278,7 @@ void testCloseApiV2() { @Test void testCloseApiClient() { HttpClient client = mock(HttpClient.class); - NessieApiClient apiClient = new NessieHttpClient(client); + NessieApiClient apiClient = new RestV1Client(client); apiClient.close(); verify(client).close(); } diff --git a/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/AbstractNessieApiHolder.java b/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/AbstractNessieApiHolder.java index d7ec528cf87..5ece3d31da2 100644 --- a/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/AbstractNessieApiHolder.java +++ b/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/AbstractNessieApiHolder.java @@ -119,8 +119,21 @@ public void close() { */ protected static AutoCloseable createNessieClient(ClassLoader classLoader, ClientKey clientKey) { try { - Class builderClazz = classLoader.loadClass(clientKey.getBuilderClass()); - Object builderInstance = builderClazz.getMethod("builder").invoke(null); + Object builderInstance; + + Class nessieClientBuilderClass = + classLoader.loadClass("org.projectnessie.client.NessieClientBuilder"); + String builderClass = clientKey.getBuilderClass(); + try { + // New functionality using NessieClientBuilder and the service loader mechanism. + Method createClientBuilderMethod = + nessieClientBuilderClass.getDeclaredMethod( + "createClientBuilder", String.class, String.class); + builderInstance = createClientBuilderMethod.invoke(null, null, builderClass); + } catch (NoSuchMethodException ignore) { + Class builderClazz = classLoader.loadClass(builderClass); + builderInstance = builderClazz.getMethod("builder").invoke(null); + } Method fromConfigMethod = builderInstance.getClass().getMethod("fromConfig", Function.class); Function getCfg = diff --git a/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/OlderNessieServersExtension.java b/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/OlderNessieServersExtension.java index eb98d57ef14..b5a4bc1e71b 100644 --- a/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/OlderNessieServersExtension.java +++ b/compatibility/common/src/main/java/org/projectnessie/tools/compatibility/internal/OlderNessieServersExtension.java @@ -27,7 +27,7 @@ import java.util.function.BooleanSupplier; import java.util.function.Function; import org.junit.jupiter.api.extension.ExtensionContext; -import org.projectnessie.client.http.v1api.HttpApiV1; +import org.projectnessie.client.rest.v1.HttpApiV1; import org.projectnessie.tools.compatibility.api.NessieBaseUri; import org.projectnessie.tools.compatibility.api.TargetVersion; import org.projectnessie.tools.compatibility.api.Version; diff --git a/compatibility/common/src/test/java/org/projectnessie/tools/compatibility/internal/TestTranslatingVersionNessieApi.java b/compatibility/common/src/test/java/org/projectnessie/tools/compatibility/internal/TestTranslatingVersionNessieApi.java index 6b25cead9a8..82688eeb732 100644 --- a/compatibility/common/src/test/java/org/projectnessie/tools/compatibility/internal/TestTranslatingVersionNessieApi.java +++ b/compatibility/common/src/test/java/org/projectnessie/tools/compatibility/internal/TestTranslatingVersionNessieApi.java @@ -35,8 +35,8 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.projectnessie.client.api.NessieApiV1; -import org.projectnessie.client.http.v1api.HttpApiV1; import org.projectnessie.client.rest.NessieInternalServerException; +import org.projectnessie.client.rest.v1.HttpApiV1; import org.projectnessie.error.BaseNessieClientServerException; import org.projectnessie.error.ErrorCode; import org.projectnessie.error.ErrorCodeAware; diff --git a/gc/gc-tool/src/main/java/org/projectnessie/gc/tool/cli/options/NessieOptions.java b/gc/gc-tool/src/main/java/org/projectnessie/gc/tool/cli/options/NessieOptions.java index 1cad6c3c357..7a76518d66d 100644 --- a/gc/gc-tool/src/main/java/org/projectnessie/gc/tool/cli/options/NessieOptions.java +++ b/gc/gc-tool/src/main/java/org/projectnessie/gc/tool/cli/options/NessieOptions.java @@ -15,14 +15,16 @@ */ package org.projectnessie.gc.tool.cli.options; -import java.lang.reflect.InvocationTargetException; +import static org.projectnessie.client.NessieClientBuilder.createClientBuilderFromSystemSettings; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_CLIENT_BUILDER_IMPL; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_CLIENT_NAME; +import static org.projectnessie.client.config.NessieClientConfigSources.mapConfigSource; + import java.net.URI; import java.util.HashMap; import java.util.Map; -import org.projectnessie.client.NessieClientBuilder; import org.projectnessie.client.api.NessieApiV1; import org.projectnessie.client.api.NessieApiV2; -import org.projectnessie.client.http.HttpClientBuilder; import org.projectnessie.gc.repository.NessieRepositoryConnector; import org.projectnessie.gc.repository.RepositoryConnector; import org.projectnessie.gc.tool.cli.Closeables; @@ -30,10 +32,16 @@ public class NessieOptions { + @CommandLine.Option( + names = "--nessie-client", + description = "Name of the Nessie client to use, defaults to HTTP suitable for REST.") + String nessieClientName; + @CommandLine.Option( names = "--nessie-api", description = - "Class name of the NessieClientBuilder to use, defaults to HttpClientBuilder suitable for REST.") + "Class name of the NessieClientBuilder implementation to use, defaults to HttpClientBuilder suitable for REST. " + + "Using this parameter is not recommended. Prefer the --nessie-client parameter instead.") String nessieApi; @CommandLine.Option( @@ -43,7 +51,7 @@ public class NessieOptions { URI nessieUri = URI.create("http://localhost:19120/api/v2"); @CommandLine.Option( - names = "--nessie-option", + names = {"-o", "--nessie-option"}, description = "Parameters to configure the NessieClientBuilder.", split = ",", arity = "0..*") @@ -54,29 +62,11 @@ public RepositoryConnector createRepositoryConnector(Closeables closeables) { } NessieApiV1 createNessieApi() { - try { - NessieClientBuilder clientBuilder; - if (nessieApi != null) { - clientBuilder = - (NessieClientBuilder) - Class.forName(nessieApi) - .asSubclass(NessieClientBuilder.class) - .getDeclaredMethod("builder") - .invoke(null); - } else { - clientBuilder = HttpClientBuilder.builder(); - } - - return clientBuilder - .withUri(nessieUri) - .fromSystemProperties() - .fromConfig(nessieOptions::get) - .build(NessieApiV2.class); - } catch (ClassNotFoundException - | IllegalAccessException - | InvocationTargetException - | NoSuchMethodException e) { - throw new RuntimeException(e); - } + Map baseConfig = new HashMap<>(nessieOptions); + baseConfig.put(CONF_NESSIE_CLIENT_NAME, nessieClientName); + baseConfig.put(CONF_NESSIE_CLIENT_BUILDER_IMPL, nessieApi); + return createClientBuilderFromSystemSettings(mapConfigSource(baseConfig)) + .withUri(nessieUri) + .build(NessieApiV2.class); } } diff --git a/integrations/iceberg-views/src/main/java/org/apache/iceberg/nessie/NessieViewCatalog.java b/integrations/iceberg-views/src/main/java/org/apache/iceberg/nessie/NessieViewCatalog.java index f156037f37d..0497126c279 100644 --- a/integrations/iceberg-views/src/main/java/org/apache/iceberg/nessie/NessieViewCatalog.java +++ b/integrations/iceberg-views/src/main/java/org/apache/iceberg/nessie/NessieViewCatalog.java @@ -100,8 +100,8 @@ public void initialize(String inputName, Map options) { this.reference = loadReference(requestedRef, hashOnRef); } - private static NessieClientBuilder createNessieClientBuilder(String customBuilder) { - NessieClientBuilder clientBuilder; + private static NessieClientBuilder createNessieClientBuilder(String customBuilder) { + NessieClientBuilder clientBuilder; if (customBuilder != null) { try { clientBuilder = diff --git a/perftest/gatling/src/main/scala/org/projectnessie/perftest/gatling/NessieProtocol.scala b/perftest/gatling/src/main/scala/org/projectnessie/perftest/gatling/NessieProtocol.scala index c2d252baed3..8e6a59b3b1e 100644 --- a/perftest/gatling/src/main/scala/org/projectnessie/perftest/gatling/NessieProtocol.scala +++ b/perftest/gatling/src/main/scala/org/projectnessie/perftest/gatling/NessieProtocol.scala @@ -21,8 +21,8 @@ import io.gatling.core.config.GatlingConfiguration import io.gatling.core.protocol.{Protocol, ProtocolComponents, ProtocolKey} import io.gatling.core.scenario.Simulation import io.gatling.core.session.Session +import org.projectnessie.client.NessieClientBuilder import org.projectnessie.client.api.NessieApiV2 -import org.projectnessie.client.http.HttpClientBuilder case class NessieProtocolBuilder() extends StrictLogging { @@ -35,9 +35,8 @@ case class NessieProtocolBuilder() extends StrictLogging { def clientFromSystemProperties(): NessieProtocol = client( - HttpClientBuilder - .builder() - .fromSystemProperties() + NessieClientBuilder + .createClientBuilderFromSystemSettings() .build(classOf[NessieApiV2]) ) } diff --git a/perftest/simulations/src/gatling/scala/org/projectnessie/perftest/gatling/CreateManyBranchesSimulation.scala b/perftest/simulations/src/gatling/scala/org/projectnessie/perftest/gatling/CreateManyBranchesSimulation.scala index a6e5fbbadd9..75477ca4b1e 100644 --- a/perftest/simulations/src/gatling/scala/org/projectnessie/perftest/gatling/CreateManyBranchesSimulation.scala +++ b/perftest/simulations/src/gatling/scala/org/projectnessie/perftest/gatling/CreateManyBranchesSimulation.scala @@ -18,8 +18,6 @@ package org.projectnessie.perftest.gatling import io.gatling.core.Predef._ import io.gatling.core.scenario.Simulation import io.gatling.core.structure.{ChainBuilder, ScenarioBuilder} -import org.projectnessie.client.api.NessieApiV1 -import org.projectnessie.client.http.HttpClientBuilder import org.projectnessie.model._ import org.projectnessie.perftest.gatling.Predef.nessie diff --git a/servers/jax-rs-testextension/src/test/java/org/projectnessie/jaxrs/ext/customclient/IncompleteClientBuilderForTesting.java b/servers/jax-rs-testextension/src/test/java/org/projectnessie/jaxrs/ext/customclient/IncompleteClientBuilderForTesting.java new file mode 100644 index 00000000000..a5dcf3d7f65 --- /dev/null +++ b/servers/jax-rs-testextension/src/test/java/org/projectnessie/jaxrs/ext/customclient/IncompleteClientBuilderForTesting.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.jaxrs.ext.customclient; + +import java.lang.reflect.Proxy; +import org.projectnessie.client.NessieClientBuilder; +import org.projectnessie.client.api.NessieApi; +import org.projectnessie.client.api.NessieApiV2; + +public class IncompleteClientBuilderForTesting + extends NessieClientBuilder.AbstractNessieClientBuilder { + @Override + public String name() { + return "IncompleteForTesting"; + } + + @Override + public int priority() { + return 0; + } + + @SuppressWarnings("unchecked") + @Override + public API build(Class apiContract) { + return (API) + Proxy.newProxyInstance( + IncompleteClientBuilderForTesting.class.getClassLoader(), + new Class[] {NessieApiV2.class}, + (proxy, method, args) -> { + throw new UnsupportedOperationException( + "This Nessie client instance is not real, sorry."); + }); + } +} diff --git a/servers/jax-rs-testextension/src/test/java/org/projectnessie/jaxrs/ext/customclient/TestCustomNessieClient.java b/servers/jax-rs-testextension/src/test/java/org/projectnessie/jaxrs/ext/customclient/TestCustomNessieClient.java new file mode 100644 index 00000000000..c65ee125d14 --- /dev/null +++ b/servers/jax-rs-testextension/src/test/java/org/projectnessie/jaxrs/ext/customclient/TestCustomNessieClient.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 Dremio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.projectnessie.jaxrs.ext.customclient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.projectnessie.jaxrs.ext.NessieJaxRsExtension.jaxRsExtension; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.projectnessie.client.api.NessieApiV1; +import org.projectnessie.client.ext.NessieApiVersions; +import org.projectnessie.client.ext.NessieClientFactory; +import org.projectnessie.client.ext.NessieClientNameResolver; +import org.projectnessie.jaxrs.ext.NessieJaxRsExtension; +import org.projectnessie.versioned.storage.common.persist.Persist; +import org.projectnessie.versioned.storage.inmemory.InmemoryBackendTestFactory; +import org.projectnessie.versioned.storage.testextension.NessieBackend; +import org.projectnessie.versioned.storage.testextension.NessiePersist; +import org.projectnessie.versioned.storage.testextension.PersistExtension; + +@ExtendWith(PersistExtension.class) +@NessieBackend(InmemoryBackendTestFactory.class) +@NessieApiVersions // all versions +public class TestCustomNessieClient implements NessieClientNameResolver { + @NessiePersist static Persist persist; + + @RegisterExtension static NessieJaxRsExtension server = jaxRsExtension(() -> persist); + + @Override + public String nessieClientName() { + return "IncompleteForTesting"; + } + + private NessieApiV1 api; + + @BeforeEach + void initApi(NessieClientFactory clientFactory) { + this.api = clientFactory.make(); + } + + @Test + void customClient() { + assertThat(api).isInstanceOf(NessieApiV1.class); + assertThatThrownBy(api::getConfig) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("This Nessie client instance is not real, sorry."); + } +} diff --git a/servers/jax-rs-testextension/src/test/resources/META-INF/services/org.projectnessie.client.NessieClientBuilder b/servers/jax-rs-testextension/src/test/resources/META-INF/services/org.projectnessie.client.NessieClientBuilder new file mode 100644 index 00000000000..39860bfb6c5 --- /dev/null +++ b/servers/jax-rs-testextension/src/test/resources/META-INF/services/org.projectnessie.client.NessieClientBuilder @@ -0,0 +1,16 @@ +# +# Copyright (C) 2023 Dremio +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +org.projectnessie.jaxrs.ext.customclient.IncompleteClientBuilderForTesting diff --git a/servers/jax-rs-tests/src/test/java/org/projectnessie/jaxrs/tests/TestRestInMemoryNaiveClient.java b/servers/jax-rs-tests/src/test/java/org/projectnessie/jaxrs/tests/TestRestInMemoryNaiveClient.java index 7f6d0e6e52f..4f97b36ac86 100644 --- a/servers/jax-rs-tests/src/test/java/org/projectnessie/jaxrs/tests/TestRestInMemoryNaiveClient.java +++ b/servers/jax-rs-tests/src/test/java/org/projectnessie/jaxrs/tests/TestRestInMemoryNaiveClient.java @@ -63,8 +63,7 @@ class TestRestInMemoryNaiveClient extends BaseTestNessieApi implements NessieCli private boolean headersProcessed; @Override - public NessieClientBuilder configure( - NessieClientBuilder builder, NessieApiVersion apiVersion) { + public NessieClientBuilder configure(NessieClientBuilder builder, NessieApiVersion apiVersion) { // Intentionally remove the `Accept` header from requests. // Service endpoints should declare the content type for their return values, // which should allow the Web Container to properly format output even in the absence diff --git a/servers/quarkus-server/src/test/java/org/projectnessie/server/TestQuarkusEventsEnabledAuthEnabled.java b/servers/quarkus-server/src/test/java/org/projectnessie/server/TestQuarkusEventsEnabledAuthEnabled.java index 71a02705f58..a387ced4fdd 100644 --- a/servers/quarkus-server/src/test/java/org/projectnessie/server/TestQuarkusEventsEnabledAuthEnabled.java +++ b/servers/quarkus-server/src/test/java/org/projectnessie/server/TestQuarkusEventsEnabledAuthEnabled.java @@ -55,8 +55,8 @@ public Map getConfigOverrides() { @Inject Instance registries; @Override - protected NessieClientBuilder customizeClient( - NessieClientBuilder builder, NessieApiVersion apiVersion) { + protected NessieClientBuilder customizeClient( + NessieClientBuilder builder, NessieApiVersion apiVersion) { return builder.withAuthentication(BasicAuthenticationProvider.create("test_user", "test_user")); } diff --git a/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/AbstractQuarkusEvents.java b/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/AbstractQuarkusEvents.java index 1c255ca17ae..695fc9b7861 100644 --- a/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/AbstractQuarkusEvents.java +++ b/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/AbstractQuarkusEvents.java @@ -97,8 +97,8 @@ void setRestUri(@NessieClientUri URI uri) { RestAssured.port = uri.getPort(); } - protected NessieClientBuilder customizeClient( - NessieClientBuilder builder, NessieApiVersion apiVersion) { + protected NessieClientBuilder customizeClient( + NessieClientBuilder builder, NessieApiVersion apiVersion) { return builder; } diff --git a/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/BaseClientAuthTest.java b/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/BaseClientAuthTest.java index f12b8f1c68b..39cf03eefb7 100644 --- a/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/BaseClientAuthTest.java +++ b/servers/quarkus-server/src/testFixtures/java/org/projectnessie/server/BaseClientAuthTest.java @@ -30,7 +30,7 @@ public abstract class BaseClientAuthTest { private NessieClientFactory apiProvider; private NessieApiV1 api; - private Consumer> customizer; + private Consumer customizer; @BeforeEach void setUp(NessieClientFactory apiProvider) { @@ -45,7 +45,7 @@ void closeClient() { } } - protected void withClientCustomizer(Consumer> customizer) { + protected void withClientCustomizer(Consumer customizer) { Preconditions.checkState(api == null, "withClientCustomizer but api has already been created!"); this.customizer = customizer; } diff --git a/site/docs/develop/java.md b/site/docs/develop/java.md index c94091ddbed..e6a4c323e61 100644 --- a/site/docs/develop/java.md +++ b/site/docs/develop/java.md @@ -33,12 +33,12 @@ is `NessieApiV1`, which can be instantiated as shown below: import java.net.URI; import java.util.List; import org.projectnessie.client.api.NessieApiV1; -import org.projectnessie.client.http.HttpClientBuilder; +import org.projectnessie.client.NessieClientBuilder; import org.projectnessie.model.Reference; -NessieApiV1 api = HttpClientBuilder.builder() - .withUri(URI.create("http://localhost:19121/api/v1")) - .build(NessieApiV1.class); +NessieApiV2 api = NessieClientBuilder.builder() + .withUri(URI.create("http://localhost:19121/api/v2")) + .build(NessieApiV2.class); List references = api.getAllReferences().get(); references.stream() diff --git a/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/ContentGenerator.java b/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/ContentGenerator.java index a9979d1d4e4..72630fad4ba 100644 --- a/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/ContentGenerator.java +++ b/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/ContentGenerator.java @@ -15,6 +15,14 @@ */ package org.projectnessie.tools.contentgenerator.cli; +import static org.projectnessie.client.NessieClientBuilder.createClientBuilderFromSystemSettings; +import static org.projectnessie.client.NessieConfigConstants.CONF_NESSIE_CLIENT_NAME; +import static org.projectnessie.client.config.NessieClientConfigSources.mapConfigSource; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import org.projectnessie.client.NessieClientBuilder; import org.projectnessie.client.api.NessieApiV2; import picocli.CommandLine; @@ -35,5 +43,40 @@ }) public abstract class ContentGenerator { - public abstract API createNessieApiInstance(); + @CommandLine.Option( + names = {"-u", "--uri"}, + scope = CommandLine.ScopeType.INHERIT, + description = "Nessie API endpoint URI, defaults to http://localhost:19120/api/v2.") + private URI uri = URI.create("http://localhost:19120/api/v2"); + + @CommandLine.Option( + names = "--nessie-client", + description = "Name of the Nessie client to use, defaults to HTTP suitable for REST.") + private String nessieClientName; + + @CommandLine.Option( + names = {"-o", "--nessie-option"}, + description = "Parameters to configure the NessieClientBuilder.", + split = ",", + arity = "0..*") + private Map nessieOptions = new HashMap<>(); + + public API createNessieApiInstance() { + @SuppressWarnings("unchecked") + API api = (API) createNessieClientBuilder().build(NessieApiV2.class); + return api; + } + + public NessieClientBuilder createNessieClientBuilder() { + Map mainConfig = new HashMap<>(nessieOptions); + if (nessieClientName != null) { + mainConfig.put(CONF_NESSIE_CLIENT_NAME, nessieClientName); + } + NessieClientBuilder clientBuilder = + createClientBuilderFromSystemSettings(mapConfigSource(mainConfig)); + if (uri != null) { + clientBuilder.withUri(uri); + } + return clientBuilder; + } } diff --git a/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/NessieContentGenerator.java b/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/NessieContentGenerator.java index ca4f9f2d6fa..7f5dad46908 100644 --- a/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/NessieContentGenerator.java +++ b/tools/content-generator/src/main/java/org/projectnessie/tools/contentgenerator/cli/NessieContentGenerator.java @@ -17,36 +17,17 @@ import com.google.common.annotations.VisibleForTesting; import java.io.PrintWriter; -import java.net.URI; -import org.projectnessie.client.NessieClientBuilder; import org.projectnessie.client.api.NessieApiV2; -import org.projectnessie.client.http.HttpClientBuilder; import org.projectnessie.client.http.HttpClientException; import org.projectnessie.error.BaseNessieClientServerException; import picocli.CommandLine; public class NessieContentGenerator extends ContentGenerator { - @CommandLine.Option( - names = {"-u", "--uri"}, - scope = CommandLine.ScopeType.INHERIT, - description = "Nessie API endpoint URI, defaults to http://localhost:19120/api/v2.") - private URI uri = URI.create("http://localhost:19120/api/v2"); - public static void main(String[] arguments) { System.exit(runMain(arguments)); } - @Override - public NessieApiV2 createNessieApiInstance() { - NessieClientBuilder clientBuilder = HttpClientBuilder.builder(); - clientBuilder.fromSystemProperties(); - if (uri != null) { - clientBuilder.withUri(uri); - } - return clientBuilder.build(NessieApiV2.class); - } - @VisibleForTesting public static int runMain(String[] arguments) { return runMain(null, arguments); @@ -59,8 +40,16 @@ public static int runMain(PrintWriter out, String[] arguments) { @VisibleForTesting public static int runMain(PrintWriter out, PrintWriter err, String[] arguments) { + NessieContentGenerator command = new NessieContentGenerator(); + return runMain(command, out, err, arguments); + } + + @VisibleForTesting + public static int runMain( + NessieContentGenerator command, PrintWriter out, PrintWriter err, String[] arguments) { CommandLine commandLine = - new CommandLine(new NessieContentGenerator()) + new CommandLine(command) + .setExecutionStrategy(command::execute) .setExecutionExceptionHandler( (ex, cmd, parseResult) -> { if (ex instanceof BaseNessieClientServerException @@ -88,4 +77,8 @@ public static int runMain(PrintWriter out, PrintWriter err, String[] arguments) commandLine.getErr().flush(); } } + + protected int execute(CommandLine.ParseResult parseResult) { + return new CommandLine.RunLast().execute(parseResult); + } } diff --git a/tools/content-generator/src/testFixtures/java/org/projectnessie/tools/contentgenerator/AbstractContentGeneratorTest.java b/tools/content-generator/src/testFixtures/java/org/projectnessie/tools/contentgenerator/AbstractContentGeneratorTest.java index 3cb2b163a9e..d16e934af1f 100644 --- a/tools/content-generator/src/testFixtures/java/org/projectnessie/tools/contentgenerator/AbstractContentGeneratorTest.java +++ b/tools/content-generator/src/testFixtures/java/org/projectnessie/tools/contentgenerator/AbstractContentGeneratorTest.java @@ -17,11 +17,11 @@ import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static org.projectnessie.client.NessieClientBuilder.createClientBuilderFromSystemSettings; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.projectnessie.client.api.NessieApiV2; -import org.projectnessie.client.http.HttpClientBuilder; import org.projectnessie.error.NessieConflictException; import org.projectnessie.error.NessieNotFoundException; import org.projectnessie.model.Branch; @@ -93,9 +93,6 @@ protected Branch makeCommit(NessieApiV2 api) } protected NessieApiV2 buildNessieApi() { - return HttpClientBuilder.builder() - .fromSystemProperties() - .withUri(NESSIE_API_URI) - .build(NessieApiV2.class); + return createClientBuilderFromSystemSettings().withUri(NESSIE_API_URI).build(NessieApiV2.class); } }