/* * Copyright 2014 the original author or 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 org.springframework.integration.x.kafka; import java.util.Arrays; import javax.validation.constraints.AssertTrue; import org.springframework.util.StringUtils; import org.springframework.xd.module.options.spi.Mixin; import org.springframework.xd.module.options.spi.ModuleOption; import org.springframework.xd.module.options.spi.ModulePlaceholders; import org.springframework.xd.module.options.spi.ProfileNamesProvider; /** * Module options for Kafka source module. * * @author Ilayaperumal Gopinathan * @author Marius Bogoevici */ @Mixin({KafkaZKOptionMixin.class, KafkaConsumerOptionsMixin.class, KafkaOffsetTopicOptionsMixin.class}) public class KafkaSourceModuleOptionsMetadata implements ProfileNamesProvider { private String topic = ""; private String topics = ""; private String partitions = ""; private String initialOffsets = ""; private OffsetStorageStrategy offsetStorage = OffsetStorageStrategy.kafka; private int streams = 1; private String groupId = ModulePlaceholders.XD_STREAM_NAME; private String encoding = "UTF8"; private int offsetUpdateTimeWindow = 10000; private int offsetUpdateCount = 0; private int offsetUpdateShutdownTimeout = 2000; private int queueSize = 8192; @ModuleOption("single topic name") public void setTopic(String topic) { this.topic = topic; } public String getTopic() { return this.topic; } @ModuleOption("comma separated kafka topic names") public void setTopics(String topics) { this.topics = topics; } public String getTopics() { return this.topics; } public String getPartitions() { return partitions; } @ModuleOption("comma separated list of partition IDs to listen on") public void setPartitions(String partitions) { this.partitions = partitions; } public String getInitialOffsets() { return initialOffsets; } @ModuleOption("comma separated list of <partition>@<offset> pairs indicating where the source should" + " start consuming from") public void setInitialOffsets(String initialOffsets) { this.initialOffsets = initialOffsets; } public OffsetStorageStrategy getOffsetStorage() { return offsetStorage; } @ModuleOption("strategy for persisting offset values") public void setOffsetStorage(OffsetStorageStrategy offsetStorage) { this.offsetStorage = offsetStorage; } @ModuleOption("number of streams in the topic") public void setStreams(int streams) { this.streams = streams; } public int getStreams() { return this.streams; } @ModuleOption("kafka consumer configuration group id") public void setGroupId(String groupId) { this.groupId = groupId; } public String getGroupId() { return this.groupId; } @ModuleOption("string encoder to translate bytes into string") public void setEncoding(String encoding) { this.encoding = encoding; } public String getEncoding() { return this.encoding; } @ModuleOption("frequency (in milliseconds) with which offsets are persisted " + "mutually exclusive with the count-based offset update option (use 0 to disable either)") public void setOffsetUpdateTimeWindow(int offsetUpdateTimeWindow) { this.offsetUpdateTimeWindow = offsetUpdateTimeWindow; } public int getOffsetUpdateTimeWindow() { return offsetUpdateTimeWindow; } @ModuleOption("frequency, in number of messages, with which offsets are persisted, per concurrent processor, " + "mutually exclusive with the time-based offset update option (use 0 to disable either)") public void setOffsetUpdateCount(int offsetUpdateCount) { this.offsetUpdateCount = offsetUpdateCount; } public int getOffsetUpdateCount() { return offsetUpdateCount; } @ModuleOption(value = "timeout for ensuring that all offsets have been written, on shutdown", hidden = true) public void setOffsetUpdateShutdownTimeout(int offsetUpdateShutdownTimeout) { this.offsetUpdateShutdownTimeout = offsetUpdateShutdownTimeout; } public int getOffsetUpdateShutdownTimeout() { return offsetUpdateShutdownTimeout; } @ModuleOption(value = "the maximum number of messages held internally and waiting for processing, " + "per concurrent handler. Value must be a power of 2") public void setQueueSize(int queueSize) { this.queueSize = queueSize; } public int getQueueSize() { return queueSize; } public enum OffsetStorageStrategy { inmemory, redis, kafka, kafkaNative } @AssertTrue(message = "the options topic and topics are mutually exclusive") public boolean isTopicOptionValid() { boolean isTopicValid = StringUtils.hasText(topic); boolean isTopicsValid = StringUtils.hasText(topics); boolean isOptionValid = isTopicValid ? !isTopicsValid : true; if (isOptionValid && (!isTopicsValid && !isTopicValid)) { this.topic = ModulePlaceholders.XD_STREAM_NAME; this.topics = this.topic; } else if (isOptionValid && isTopicValid) { this.topics = this.topic; } return isOptionValid; } @AssertTrue(message = "explicit partitions can only be set when using single topic source") public boolean isPartitionsOptionValid() { return (Arrays.asList(topics.split("\\s*,\\s*")).size() > 1) ? !StringUtils.hasText(partitions) : true; } @AssertTrue(message = "initial offsets can only be set when using single topic source") public boolean isInitialOffsetsValid() { return (Arrays.asList(topics.split("\\s*,\\s*")).size() > 1) ? !StringUtils.hasText(initialOffsets) : true; } @Override public String[] profilesToActivate() { if (offsetStorage != null) { return new String[] {String.format("%s-offset-manager", offsetStorage)}; } else { throw new IllegalStateException("An offset storage strategy must be configured"); } } }