All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.servicetalk.http.netty.ClientStrategyInfluencerChainBuilder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2019, 2021-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.http.netty;

import io.servicetalk.client.api.ConnectionFactoryFilter;
import io.servicetalk.http.api.ConnectAndHttpExecutionStrategy;
import io.servicetalk.http.api.FilterableStreamingHttpConnection;
import io.servicetalk.http.api.HttpExecutionStrategy;
import io.servicetalk.http.api.HttpLoadBalancerFactory;
import io.servicetalk.http.api.StreamingHttpClientFilterFactory;
import io.servicetalk.http.api.StreamingHttpConnectionFilterFactory;
import io.servicetalk.transport.api.ConnectExecutionStrategy;
import io.servicetalk.transport.api.ExecutionStrategy;
import io.servicetalk.transport.api.ExecutionStrategyInfluencer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;

import static io.servicetalk.http.api.HttpExecutionStrategies.defaultStrategy;
import static io.servicetalk.http.api.HttpExecutionStrategies.offloadAll;
import static io.servicetalk.http.api.HttpExecutionStrategies.offloadNever;
import static io.servicetalk.http.api.HttpExecutionStrategies.offloadNone;

final class ClientStrategyInfluencerChainBuilder {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClientStrategyInfluencerChainBuilder.class);

    @Nullable
    private ConnectAndHttpExecutionStrategy connFactoryChain;
    @Nullable
    private HttpExecutionStrategy connFilterChain;
    @Nullable
    private HttpExecutionStrategy clientChain;

    ClientStrategyInfluencerChainBuilder() {
        connFactoryChain = null;
        connFilterChain = null;
        clientChain = null;
    }

    private ClientStrategyInfluencerChainBuilder(ClientStrategyInfluencerChainBuilder from) {
        connFactoryChain = from.connFactoryChain;
        connFilterChain = from.connFilterChain;
        clientChain = from.clientChain;
    }

    void add(StreamingHttpClientFilterFactory clientFilter) {
        add(StreamingHttpClientFilterFactory.class, clientFilter, clientFilter.requiredOffloads());
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    void add(HttpLoadBalancerFactory lb) {
        add(HttpLoadBalancerFactory.class, (HttpLoadBalancerFactory) lb, lb.requiredOffloads());
    }

    private > void add(
            final Class clazz, final T influencer, HttpExecutionStrategy strategy) {
        if (offloadNever() == strategy) {
            offloadNeverWarning(clazz, influencer);
            strategy = offloadNone();
        }
        if (defaultStrategy() == strategy) {
            defaultStrategyWarning(clazz, influencer);
            strategy = offloadAll();
        }
        @Nullable
        final HttpExecutionStrategy clientChain = this.clientChain;
        this.clientChain = null != clientChain ? clientChain.merge(strategy) : strategy;
        logIfChanges(clazz, influencer, clientChain, this.clientChain);
    }

    void add(ConnectionFactoryFilter connectionFactoryFilter) {
        ExecutionStrategy filterOffloads = connectionFactoryFilter.requiredOffloads();
        if (offloadNever() == filterOffloads) {
            offloadNeverWarning(ConnectionFactoryFilter.class, connectionFactoryFilter);
            filterOffloads = offloadNone();
        }
        if (defaultStrategy() == filterOffloads) {
            defaultStrategyWarning(ConnectionFactoryFilter.class, connectionFactoryFilter);
            filterOffloads = offloadAll();
        }
        @Nullable
        final ConnectAndHttpExecutionStrategy connFactoryChain = this.connFactoryChain;
        this.connFactoryChain = null != connFactoryChain ?
                 connFactoryChain.merge(filterOffloads) : ConnectAndHttpExecutionStrategy.from(filterOffloads);
        logIfChanges(ConnectionFactoryFilter.class, connectionFactoryFilter, connFactoryChain, this.connFactoryChain);
    }

    void add(StreamingHttpConnectionFilterFactory connectionFilter) {
        HttpExecutionStrategy filterOffloads = connectionFilter.requiredOffloads();
        if (offloadNever() == filterOffloads) {
            offloadNeverWarning(StreamingHttpConnectionFilterFactory.class, connectionFilter);
            filterOffloads = offloadNone();
        }
        if (defaultStrategy() == filterOffloads) {
            defaultStrategyWarning(StreamingHttpConnectionFilterFactory.class, connectionFilter);
            filterOffloads = offloadAll();
        }
        if (filterOffloads.hasOffloads()) {
            @Nullable
            final HttpExecutionStrategy connFilterChain = this.connFilterChain;
            this.connFilterChain = null != connFilterChain ? connFilterChain.merge(filterOffloads) : filterOffloads;
            logIfChanges(StreamingHttpConnectionFilterFactory.class,
                    connectionFilter, connFilterChain, this.connFilterChain);
        }
    }

    HttpExecutionStrategy buildForClient(HttpExecutionStrategy builderStrategy) {
        HttpExecutionStrategy chainStrategy = clientChain;
        if (null != connFilterChain) {
            chainStrategy = null != chainStrategy ? chainStrategy.merge(connFilterChain) : connFilterChain;
        }
        if (null != connFactoryChain) {
            HttpExecutionStrategy connectionFactoryStrategy = HttpExecutionStrategy.from(buildForConnectionFactory());
            chainStrategy = null != chainStrategy ?
                    chainStrategy.merge(connectionFactoryStrategy) : connectionFactoryStrategy;
        }

        return (null == chainStrategy || !chainStrategy.hasOffloads()) ?
                builderStrategy :
                defaultStrategy() == builderStrategy ?
                        chainStrategy : builderStrategy.hasOffloads() ?
                            chainStrategy.merge(builderStrategy) : builderStrategy;
    }

    ExecutionStrategy buildForConnectionFactory() {
        return null == connFactoryChain ?
                ExecutionStrategy.offloadNone() :
                defaultStrategy() != connFactoryChain.httpStrategy() ?
                        ConnectExecutionStrategy.offloadNone() != connFactoryChain.connectStrategy() ?
                                connFactoryChain : connFactoryChain.httpStrategy() :
                        ConnectExecutionStrategy.offloadNone() != connFactoryChain.connectStrategy() ?
                                connFactoryChain.connectStrategy() : ExecutionStrategy.offloadNone();
    }

    ClientStrategyInfluencerChainBuilder copy() {
        return new ClientStrategyInfluencerChainBuilder(this);
    }

    private static > void offloadNeverWarning(final Class clazz,
                                                                                       final T influencer) {
        LOGGER.warn("{}#requiredOffloads() returns offloadNever(), which is unexpected. offloadNone() should be used " +
                        "instead. Making automatic adjustment, update the {} to avoid this warning.",
                influencer, clazz.getSimpleName());
    }

    private static > void defaultStrategyWarning(final Class clazz,
                                                                                          final T influencer) {
        LOGGER.warn("{}#requiredOffloads() returns defaultStrategy(), which is unexpected. " +
                        "offloadAll() (safe default) or more appropriate custom strategy should be used instead." +
                        "Making automatic adjustment, update the {} to avoid this warning.",
                influencer, clazz.getSimpleName());
    }

    private static > void logIfChanges(final Class clazz,
                final T influencer, @Nullable final ExecutionStrategy before, @Nullable final ExecutionStrategy after) {
        if (before != after) {
            LOGGER.debug("{} '{}' changes execution strategy from '{}' to '{}'", clazz, influencer, before, after);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy