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

org.eclipse.ditto.connectivity.service.messaging.kafka.PropertiesFactory Maven / Gradle / Ivy

There is a newer version: 3.5.10
Show newest version
/*
 * Copyright (c) 2017 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.ditto.connectivity.service.messaging.kafka;

import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.config.SslConfigs;
import org.apache.kafka.common.serialization.ByteBufferDeserializer;
import org.apache.kafka.common.serialization.ByteBufferSerializer;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.service.config.KafkaConfig;

import com.typesafe.config.Config;

import akka.kafka.CommitterSettings;
import akka.kafka.ConnectionCheckerSettings;
import akka.kafka.ConsumerSettings;
import akka.kafka.ProducerSettings;

/**
 * Creates Kafka properties from a given {@link org.eclipse.ditto.connectivity.model.Connection} configuration.
 */
final class PropertiesFactory {

    private final Collection commonSpecificConfigs;
    private final Collection consumerSpecificConfigs;
    private final Collection producerSpecificConfigs;

    private final Connection connection;
    private final KafkaConfig config;
    private final String clientId;
    private final String bootstrapServers;

    private PropertiesFactory(final Connection connection,
            final KafkaConfig config,
            final String clientId) {

        this.connection = checkNotNull(connection, "connection");
        this.config = checkNotNull(config, "config");
        this.clientId = checkNotNull(clientId, "clientId");
        bootstrapServers = KafkaBootstrapServerSpecificConfig.getInstance().getBootstrapServers(connection);
        commonSpecificConfigs = List.of(KafkaAuthenticationSpecificConfig.getInstance(),
                KafkaBootstrapServerSpecificConfig.getInstance());
        consumerSpecificConfigs = getConsumerSpecificConfigs(commonSpecificConfigs);
        producerSpecificConfigs = List.copyOf(commonSpecificConfigs);
    }

    private static Collection getConsumerSpecificConfigs(
            final Collection commonSpecificConfigs) {
        final Collection consumerSpecificConfigs = new ArrayList<>(commonSpecificConfigs);
        consumerSpecificConfigs.add(KafkaConsumerGroupSpecificConfig.getInstance());
        consumerSpecificConfigs.add(KafkaConsumerOffsetResetSpecificConfig.getInstance());
        return List.copyOf(consumerSpecificConfigs);
    }

    /**
     * Returns an instance of the factory.
     *
     * @param connection the Kafka connection.
     * @param config the Kafka configuration settings.
     * @param clientId the client ID.
     * @return the instance.
     * @throws NullPointerException if any argument is {@code null}.
     */
    static PropertiesFactory newInstance(final Connection connection,
            final KafkaConfig config,
            final String clientId) {

        return new PropertiesFactory(connection, config, clientId);
    }

    /**
     * Returns settings for a kafka consumer.
     *
     * @return the settings.
     */
    ConsumerSettings getConsumerSettings(final boolean dryRun) {
        final Config alpakkaConfig = config.getConsumerConfig().getAlpakkaConfig();
        final ConnectionCheckerSettings connectionCheckerSettings =
                ConnectionCheckerSettings.apply(alpakkaConfig.getConfig("connection-checker"));
        final ConsumerSettings consumerSettings =
                ConsumerSettings.apply(alpakkaConfig, new StringDeserializer(), new ByteBufferDeserializer())
                        .withBootstrapServers(bootstrapServers)
                        .withGroupId(connection.getId().toString())
                        .withClientId(clientId + "-consumer")
                        .withProperties(getTrustedSelfSignedCertificates())
                        .withProperties(getConsumerSpecificConfigProperties())
                        .withProperties(getSecurityProtocolProperties())
                        .withConnectionChecker(connectionCheckerSettings);

        // disable auto commit in dry run mode
        return dryRun ? consumerSettings.withProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false") :
                consumerSettings;
    }

    CommitterSettings getCommitterSettings() {
        final Config committerConfig = config.getCommitterConfig().getAlpakkaConfig();
        return CommitterSettings.apply(committerConfig);
    }

    ProducerSettings getProducerSettings() {
        final Config alpakkaConfig = config.getProducerConfig().getAlpakkaConfig();
        return ProducerSettings.apply(alpakkaConfig, new StringSerializer(), new ByteBufferSerializer())
                .withBootstrapServers(bootstrapServers)
                .withProperties(getClientIdProperties())
                .withProperties(getTrustedSelfSignedCertificates())
                .withProperties(getProducerSpecificConfigProperties())
                .withProperties(getSecurityProtocolProperties());
    }

    private Map getTrustedSelfSignedCertificates() {
        if (connection.isValidateCertificates() && connection.getTrustedCertificates().isPresent()) {
            return Map.of(SslConfigs.SSL_TRUSTSTORE_TYPE_CONFIG, "PEM",
                    SslConfigs.SSL_TRUSTSTORE_CERTIFICATES_CONFIG, connection.getTrustedCertificates().orElse(""),
                    SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, "");
        }
        return Map.of();
    }

    private Map getClientIdProperties() {
        return Map.of(CommonClientConfigs.CLIENT_ID_CONFIG, clientId + "-producer");
    }

    private Map getConsumerSpecificConfigProperties() {
        final Map properties = new HashMap<>();
        for (final KafkaSpecificConfig specificConfig : consumerSpecificConfigs) {
            properties.putAll(specificConfig.apply(connection));
        }
        return properties;
    }

    private Map getProducerSpecificConfigProperties() {
        final Map properties = new HashMap<>();
        for (final KafkaSpecificConfig specificConfig : producerSpecificConfigs) {
            properties.putAll(specificConfig.apply(connection));
        }
        return properties;
    }

    private Map getSecurityProtocolProperties() {
        if (isConnectionAuthenticated()) {
            return addAuthenticatedSecurityProtocol();
        } else {
            return addUnauthenticatedSecurityProtocol();
        }
    }

    private boolean isConnectionAuthenticated() {
        final KafkaSpecificConfig authenticationSpecificConfig =
                KafkaAuthenticationSpecificConfig.getInstance();
        return authenticationSpecificConfig.isApplicable(connection);
    }

    private Map addAuthenticatedSecurityProtocol() {
        if (isConnectionSecure()) {
            return Map.of(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
        } else {
            return Map.of(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_PLAINTEXT");
        }
    }

    private Map addUnauthenticatedSecurityProtocol() {
        if (isConnectionSecure()) {
            return Map.of(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SSL");
        } else {
            return Map.of(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "PLAINTEXT");
        }
    }

    private boolean isConnectionSecure() {
        return "ssl".equals(connection.getProtocol());
    }

    /**
     * Convert a structured Config into flat map from config paths to values.
     * Replicates Alpakka Kafka client's interpretation of client Config.
     *
     * @param config the Config object.
     * @return flat map from config paths to values.
     */
    private static HashMap configToProperties(final Config config) {
        final HashMap flattened = new HashMap<>();
        final Map unwrapped = config.root().unwrapped();
        flattenUnwrappedConfig(unwrapped, "", flattened);
        return flattened;
    }

    /**
     * Convert an unwrapped config into a flat properties map.
     *
     * @param unwrapped Result of {@code ConfigObject#unwrapped} containing structural maps.
     * @param prefix prefix of the config path.
     * @param accumulator accumulator in which the flat path-value definitions are written.
     */
    private static void flattenUnwrappedConfig(final Map unwrapped, final String prefix,
            final HashMap accumulator) {
        unwrapped.forEach((key, value) -> {
            final String path = prefix + key;
            if (value instanceof Map) {
                flattenUnwrappedConfig((Map) value, path + ".", accumulator);
            } else {
                accumulator.put(path, value);
            }
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy