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

org.apache.kafka.clients.NodeApiVersions Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.kafka.clients;

import org.apache.kafka.common.errors.UnsupportedVersionException;
import org.apache.kafka.common.feature.SupportedVersionRange;
import org.apache.kafka.common.message.ApiVersionsResponseData.ApiVersion;
import org.apache.kafka.common.message.ApiVersionsResponseData.SupportedFeatureKey;
import org.apache.kafka.common.protocol.ApiKeys;
import org.apache.kafka.common.requests.ApiVersionsResponse;
import org.apache.kafka.common.utils.Utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

/**
 * An internal class which represents the API versions supported by a particular node.
 */
public class NodeApiVersions {

    // A map of the usable versions of each API, keyed by the ApiKeys instance
    private final Map supportedVersions = new EnumMap<>(ApiKeys.class);

    // List of APIs which the broker supports, but which are unknown to the client
    private final List unknownApis = new ArrayList<>();

    private final Map supportedFeatures;

    private final boolean zkMigrationEnabled;

    /**
     * Create a NodeApiVersions object with the current ApiVersions.
     *
     * @return A new NodeApiVersions object.
     */
    public static NodeApiVersions create() {
        return create(Collections.emptyList());
    }

    /**
     * Create a NodeApiVersions object.
     *
     * @param overrides API versions to override. Any ApiVersion not specified here will be set to the current client
     *                  value.
     * @return A new NodeApiVersions object.
     */
    public static NodeApiVersions create(Collection overrides) {
        List apiVersions = new LinkedList<>(overrides);
        for (ApiKeys apiKey : ApiKeys.clientApis()) {
            boolean exists = false;
            for (ApiVersion apiVersion : apiVersions) {
                if (apiVersion.apiKey() == apiKey.id) {
                    exists = true;
                    break;
                }
            }
            if (!exists) apiVersions.add(ApiVersionsResponse.toApiVersion(apiKey));
        }
        return new NodeApiVersions(apiVersions, Collections.emptyList(), false);
    }


    /**
     * Create a NodeApiVersions object with a single ApiKey. It is mainly used in tests.
     *
     * @param apiKey ApiKey's id.
     * @param minVersion ApiKey's minimum version.
     * @param maxVersion ApiKey's maximum version.
     * @return A new NodeApiVersions object.
     */
    public static NodeApiVersions create(short apiKey, short minVersion, short maxVersion) {
        return create(Collections.singleton(new ApiVersion()
                .setApiKey(apiKey)
                .setMinVersion(minVersion)
                .setMaxVersion(maxVersion)));
    }

    public NodeApiVersions(Collection nodeApiVersions, Collection nodeSupportedFeatures, boolean zkMigrationEnabled) {
        for (ApiVersion nodeApiVersion : nodeApiVersions) {
            if (ApiKeys.hasId(nodeApiVersion.apiKey())) {
                ApiKeys nodeApiKey = ApiKeys.forId(nodeApiVersion.apiKey());
                supportedVersions.put(nodeApiKey, nodeApiVersion);
            } else {
                // Newer brokers may support ApiKeys we don't know about
                unknownApis.add(nodeApiVersion);
            }
        }

        Map supportedFeaturesBuilder = new HashMap<>();
        for (SupportedFeatureKey supportedFeature : nodeSupportedFeatures) {
            supportedFeaturesBuilder.put(supportedFeature.name(),
                    new SupportedVersionRange(supportedFeature.minVersion(), supportedFeature.maxVersion()));
        }
        this.supportedFeatures = Collections.unmodifiableMap(supportedFeaturesBuilder);
        this.zkMigrationEnabled = zkMigrationEnabled;
    }

    /**
     * Return the most recent version supported by both the node and the local software.
     */
    public short latestUsableVersion(ApiKeys apiKey) {
        return latestUsableVersion(apiKey, apiKey.oldestVersion(), apiKey.latestVersion());
    }

    /**
     * Get the latest version supported by the broker within an allowed range of versions
     */
    public short latestUsableVersion(ApiKeys apiKey, short oldestAllowedVersion, short latestAllowedVersion) {
        if (!supportedVersions.containsKey(apiKey))
            throw new UnsupportedVersionException("The node does not support " + apiKey);
        ApiVersion supportedVersion = supportedVersions.get(apiKey);
        Optional intersectVersion = ApiVersionsResponse.intersect(supportedVersion,
            new ApiVersion()
                .setApiKey(apiKey.id)
                .setMinVersion(oldestAllowedVersion)
                .setMaxVersion(latestAllowedVersion));

        if (intersectVersion.isPresent())
            return intersectVersion.get().maxVersion();
        else
            throw new UnsupportedVersionException("The node does not support " + apiKey +
                " with version in range [" + oldestAllowedVersion + "," + latestAllowedVersion + "]. The supported" +
                " range is [" + supportedVersion.minVersion() + "," + supportedVersion.maxVersion() + "].");
    }

    /**
     * Convert the object to a string with no linebreaks.

*

* This toString method is relatively expensive, so avoid calling it unless debug logging is turned on. */ @Override public String toString() { return toString(false); } /** * Convert the object to a string. * * @param lineBreaks True if we should add a linebreak after each api. */ public String toString(boolean lineBreaks) { // The apiVersion collection may not be in sorted order. We put it into // a TreeMap before printing it out to ensure that we always print in // ascending order. TreeMap apiKeysText = new TreeMap<>(); for (ApiVersion supportedVersion : this.supportedVersions.values()) apiKeysText.put(supportedVersion.apiKey(), apiVersionToText(supportedVersion)); for (ApiVersion apiVersion : unknownApis) apiKeysText.put(apiVersion.apiKey(), apiVersionToText(apiVersion)); // Also handle the case where some apiKey types are not specified at all in the given ApiVersions, // which may happen when the remote is too old. for (ApiKeys apiKey : ApiKeys.clientApis()) { if (!apiKeysText.containsKey(apiKey.id)) { StringBuilder bld = new StringBuilder(); bld.append(apiKey.name).append("("). append(apiKey.id).append("): ").append("UNSUPPORTED"); apiKeysText.put(apiKey.id, bld.toString()); } } String separator = lineBreaks ? ",\n\t" : ", "; StringBuilder bld = new StringBuilder(); bld.append("("); if (lineBreaks) bld.append("\n\t"); bld.append(String.join(separator, apiKeysText.values())); if (lineBreaks) bld.append("\n"); bld.append(")"); return bld.toString(); } private String apiVersionToText(ApiVersion apiVersion) { StringBuilder bld = new StringBuilder(); ApiKeys apiKey = null; if (ApiKeys.hasId(apiVersion.apiKey())) { apiKey = ApiKeys.forId(apiVersion.apiKey()); bld.append(apiKey.name).append("(").append(apiKey.id).append("): "); } else { bld.append("UNKNOWN(").append(apiVersion.apiKey()).append("): "); } if (apiVersion.minVersion() == apiVersion.maxVersion()) { bld.append(apiVersion.minVersion()); } else { bld.append(apiVersion.minVersion()).append(" to ").append(apiVersion.maxVersion()); } if (apiKey != null) { ApiVersion supportedVersion = supportedVersions.get(apiKey); if (apiKey.latestVersion() < supportedVersion.minVersion()) { bld.append(" [unusable: node too new]"); } else if (supportedVersion.maxVersion() < apiKey.oldestVersion()) { bld.append(" [unusable: node too old]"); } else { short latestUsableVersion = Utils.min(apiKey.latestVersion(), supportedVersion.maxVersion()); bld.append(" [usable: ").append(latestUsableVersion).append("]"); } } return bld.toString(); } /** * Get the version information for a given API. * * @param apiKey The api key to lookup * @return The api version information from the broker or null if it is unsupported */ public ApiVersion apiVersion(ApiKeys apiKey) { return supportedVersions.get(apiKey); } public Map allSupportedApiVersions() { return supportedVersions; } public Map supportedFeatures() { return supportedFeatures; } public boolean zkMigrationEnabled() { return zkMigrationEnabled; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy