Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce HttpProviders and GrpcProviders #2137

Merged
merged 7 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project authors
*
* 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 io.servicetalk.grpc.api;

import java.time.Duration;

import static java.util.Objects.requireNonNull;

/**
* A {@link GrpcClientBuilder} that delegates all methods to another {@link GrpcClientBuilder}.
*
* @param <U> the type of address before resolution (unresolved address)
* @param <R> the type of address after resolution (resolved address)
*/
public class DelegatingGrpcClientBuilder<U, R> implements GrpcClientBuilder<U, R> {

private GrpcClientBuilder<U, R> delegate;

public DelegatingGrpcClientBuilder(final GrpcClientBuilder<U, R> delegate) {
this.delegate = requireNonNull(delegate);
}

/**
* Returns the {@link GrpcClientBuilder} delegate.
*
* @return Delegate {@link GrpcClientBuilder}.
*/
protected final GrpcClientBuilder<U, R> delegate() {
return delegate;
}

@Override
public String toString() {
return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}';
}

@Override
public GrpcClientBuilder<U, R> initializeHttp(final HttpInitializer<U, R> initializer) {
delegate = delegate.initializeHttp(initializer);
return this;
}

@Override
public GrpcClientBuilder<U, R> defaultTimeout(final Duration defaultTimeout) {
delegate = delegate.defaultTimeout(defaultTimeout);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I had all builder methods as

    @Override
    public GrpcClientBuilder<U, R> defaultTimeout(final Duration defaultTimeout) {
        delegate.defaultTimeout(defaultTimeout);
        return this;
    }

Spotbugs complained that the return value is discarded. I though to suppress it because all our builder implementations always return this. But then considered a use-case when users may have an immutable provider or builder impl that always return a new instance. LMK what you think. Should we support that and reassign the delegate or is it better to keep delegate as final?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any alternative to assigning delegate with each delegate invocation. Eventually someone will get clever and implement a builder which doesn't return this and we will have to fix it anyway. I've had to fix very non-obvious problems more than once where failing to reassign the builder at the end of the chain meant that the operations in the chain were lost. It is always a frustrating bug to locate.

return this;
}

@Override
public <Client extends GrpcClient<?>> Client build(final GrpcClientFactory<Client, ?> clientFactory) {
return delegate.build(clientFactory);
}

@Override
public <BlockingClient extends BlockingGrpcClient<?>> BlockingClient buildBlocking(
final GrpcClientFactory<?, BlockingClient> clientFactory) {
return delegate.buildBlocking(clientFactory);
}

@Override
public MultiClientBuilder buildMulti() {
return delegate.buildMulti();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project authors
*
* 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 io.servicetalk.grpc.api;

import io.servicetalk.concurrent.api.Single;

import java.time.Duration;

import static java.util.Objects.requireNonNull;

/**
* A {@link GrpcServerBuilder} that delegates all methods to another {@link GrpcServerBuilder}.
*/
public class DelegatingGrpcServerBuilder implements GrpcServerBuilder {

private GrpcServerBuilder delegate;

public DelegatingGrpcServerBuilder(final GrpcServerBuilder delegate) {
this.delegate = requireNonNull(delegate);
}

/**
* Returns the {@link GrpcServerBuilder} delegate.
*
* @return Delegate {@link GrpcServerBuilder}.
*/
protected final GrpcServerBuilder delegate() {
return delegate;
}

@Override
public String toString() {
return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}';
}

@Override
public GrpcServerBuilder initializeHttp(final HttpInitializer initializer) {
delegate = delegate.initializeHttp(initializer);
return this;
}

@Override
public GrpcServerBuilder defaultTimeout(final Duration defaultTimeout) {
delegate = delegate.defaultTimeout(defaultTimeout);
return this;
}

@Override
public GrpcServerBuilder lifecycleObserver(final GrpcLifecycleObserver lifecycleObserver) {
delegate = delegate.lifecycleObserver(lifecycleObserver);
return this;
}

@Override
public Single<GrpcServerContext> listen(final GrpcBindableService<?>... services) {
return delegate.listen(services);
}

@Override
public Single<GrpcServerContext> listen(final GrpcServiceFactory<?>... serviceFactories) {
return delegate.listen(serviceFactories);
}

@Override
public GrpcServerContext listenAndAwait(final GrpcServiceFactory<?>... serviceFactories) throws Exception {
return delegate.listenAndAwait(serviceFactories);
}

@Override
public GrpcServerContext listenAndAwait(final GrpcBindableService<?>... services) throws Exception {
return delegate.listenAndAwait(services);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright © 2022 Apple Inc. and the ServiceTalk project authors
*
* 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 io.servicetalk.grpc.api;

import io.servicetalk.http.api.HttpProviders;
import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider;
import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider;

import java.net.SocketAddress;
import java.util.ServiceLoader;

/**
* A holder for all gRPC-specific providers that can be registered using {@link ServiceLoader}.
bondolo marked this conversation as resolved.
Show resolved Hide resolved
bondolo marked this conversation as resolved.
Show resolved Hide resolved
*
* @see HttpProviders
*/
public final class GrpcProviders {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I think of the name Provider it is akin to me to Supplier, but extended from no-args to act as an abstracted virtual constructor. These interfaces feel more like Customizers to use Spring terminology. I wonder if others might be confused by the name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"provider" is a term defined by ServiceLoader javadoc. Since all "providers" are tight to ServiceLoader, using its terminology vs Spring terminology is more natural. Armeria also uses the name "provider".


private GrpcProviders() {
// No instances.
}

/**
* Provider for {@link GrpcClientBuilder}.
* <p>
* An HTTP layer should use {@link SingleAddressHttpClientBuilderProvider}.
*/
@FunctionalInterface
public interface GrpcClientBuilderProvider {

/**
* Returns a {@link GrpcClientBuilder} based on the address and pre-initialized {@link GrpcClientBuilder}.
* <p>
* This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before
* returning it, or wrap it ({@link DelegatingGrpcClientBuilder} may be helpful).
*
* @param address a remote address used to create a {@link GrpcClientBuilder}, it can be resolved or unresolved
* based on the factory used
* @param builder pre-initialized {@link GrpcClientBuilder}
* @param <U> the type of address before resolution (unresolved address)
* @param <R> the type of address after resolution (resolved address)
* @return a {@link GrpcClientBuilder} based on the address and pre-initialized {@link GrpcClientBuilder}.
* @see DelegatingGrpcClientBuilder
*/
<U, R> GrpcClientBuilder<U, R> newBuilder(U address, GrpcClientBuilder<U, R> builder);
}

/**
* Provider for {@link GrpcServerBuilder}.
* <p>
* An HTTP layer should use {@link HttpServerBuilderProvider}.
*/
@FunctionalInterface
public interface GrpcServerBuilderProvider {

/**
* Returns a {@link GrpcServerBuilder} based on the address and pre-initialized {@link GrpcServerBuilder}.
* <p>
* This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before
* returning it, or wrap it ({@link DelegatingGrpcServerBuilder} may be helpful).
*
* @param address a server address used to create a {@link GrpcServerBuilder}
* @param builder pre-initialized {@link GrpcServerBuilder}
* @return a {@link GrpcServerBuilder} based on the address and pre-initialized{@link GrpcServerBuilder}.
idelpivnitskiy marked this conversation as resolved.
Show resolved Hide resolved
* @see DelegatingGrpcServerBuilder
*/
GrpcServerBuilder newBuilder(SocketAddress address, GrpcServerBuilder builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ final class DefaultGrpcClientBuilder<U, R> implements GrpcClientBuilder<U, R> {

private final Supplier<SingleAddressHttpClientBuilder<U, R>> httpClientBuilderSupplier;

// Do not use this ctor directly, GrpcClients is the entry point for creating a new builder.
DefaultGrpcClientBuilder(final Supplier<SingleAddressHttpClientBuilder<U, R>> httpClientBuilderSupplier) {
this.httpClientBuilderSupplier = httpClientBuilderSupplier;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ final class DefaultGrpcServerBuilder implements GrpcServerBuilder, ServerBinder
@Nullable
private Duration defaultTimeout;

// Do not use this ctor directly, GrpcServers is the entry point for creating a new builder.
DefaultGrpcServerBuilder(final Supplier<HttpServerBuilder> httpServerBuilderSupplier) {
this.httpServerBuilderSupplier = () -> httpServerBuilderSupplier.get()
.protocols(h2Default()).allowDropRequestTrailers(true);
Expand Down
Loading