org.eclipse.ditto.connectivity.service.messaging.kafka.PropertiesFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ditto-connectivity-service Show documentation
Show all versions of ditto-connectivity-service Show documentation
Eclipse Ditto is a framework for creating and managing digital twins in the IoT.
/*
* 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);
}
});
}
}