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

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

/**
 * 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.protocol.ApiKeys;
import org.apache.kafka.common.protocol.ProtoUtils;
import org.apache.kafka.common.requests.ApiVersionsResponse.ApiVersion;
import org.apache.kafka.common.utils.Utils;

import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;

/**
 * An internal class which represents the API versions supported by a particular node.
 */
public class NodeApiVersions {
    private static final Short API_NOT_ON_NODE = null;
    private static final short NODE_TOO_OLD = (short) -1;
    private static final short NODE_TOO_NEW = (short) -2;
    private final Collection nodeApiVersions;

    /**
     * An array of the usable versions of each API, indexed by the ApiKeys ID.
     */
    private final Map usableVersions = new EnumMap<>(ApiKeys.class);

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

    /**
     * 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.values()) {
            boolean exists = false;
            for (ApiVersion apiVersion : apiVersions) {
                if (apiVersion.apiKey == apiKey.id) {
                    exists = true;
                    break;
                }
            }
            if (!exists) {
                apiVersions.add(new ApiVersion(apiKey.id, ProtoUtils.oldestVersion(apiKey.id),
                    ProtoUtils.latestVersion(apiKey.id)));
            }
        }
        return new NodeApiVersions(apiVersions);
    }

    public NodeApiVersions(Collection nodeApiVersions) {
        this.nodeApiVersions = nodeApiVersions;
        for (ApiVersion nodeApiVersion : nodeApiVersions) {
            int nodeApiKey = nodeApiVersion.apiKey;
            // Newer brokers may support ApiKeys we don't know about, ignore them
            if (ApiKeys.hasId(nodeApiKey)) {
                short v = Utils.min(ProtoUtils.latestVersion(nodeApiKey), nodeApiVersion.maxVersion);
                if (v < nodeApiVersion.minVersion) {
                    usableVersions.put(ApiKeys.forId(nodeApiKey), NODE_TOO_NEW);
                } else if (v < ProtoUtils.oldestVersion(nodeApiKey)) {
                    usableVersions.put(ApiKeys.forId(nodeApiKey), NODE_TOO_OLD);
                } else {
                    usableVersions.put(ApiKeys.forId(nodeApiKey), v);
                }
            }
        }
    }

    /**
     * Return the most recent version supported by both the node and the local software.
     */
    public short usableVersion(ApiKeys apiKey) {
        Short usableVersion = usableVersions.get(apiKey);
        if (usableVersion == API_NOT_ON_NODE)
            throw new UnsupportedVersionException("The broker does not support " + apiKey);
        else if (usableVersion == NODE_TOO_OLD)
            throw new UnsupportedVersionException("The broker is too old to support " + apiKey +
                " version " + ProtoUtils.oldestVersion(apiKey.id));
        else if (usableVersion == NODE_TOO_NEW)
            throw new UnsupportedVersionException("The broker is too new to support " + apiKey +
                " version " + ProtoUtils.latestVersion(apiKey.id));
        else
            return usableVersion;
    }

    /**
     * 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 apiVersion : this.nodeApiVersions) 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.values()) { 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(Utils.join(apiKeysText.values(), separator)); 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); } if (apiKey != null) { bld.append(apiKey.name).append("(").append(apiKey.id).append("): "); } else { bld.append("UNKNOWN(").append(apiKey.id).append("): "); } if (apiVersion.minVersion == apiVersion.maxVersion) { bld.append(apiVersion.minVersion); } else { bld.append(apiVersion.minVersion).append(" to ").append(apiVersion.maxVersion); } if (apiKey != null) { Short usableVersion = usableVersions.get(apiKey); if (usableVersion == NODE_TOO_OLD) bld.append(" [unusable: node too old]"); else if (usableVersion == NODE_TOO_NEW) bld.append(" [unusable: node too new]"); else bld.append(" [usable: ").append(usableVersion).append("]"); } return bld.toString(); } public ApiVersion apiVersion(ApiKeys apiKey) { for (ApiVersion nodeApiVersion : nodeApiVersions) { if (nodeApiVersion.apiKey == apiKey.id) { return nodeApiVersion; } } throw new NoSuchElementException(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy