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

com.arpnetworking.metrics.common.kafka.RunnableConsumerImpl Maven / Gradle / Ivy

There is a newer version: 1.22.6
Show newest version
/*
 * Copyright 2019 Dropbox.com
 *
 * 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 com.arpnetworking.metrics.common.kafka;

import com.arpnetworking.commons.builder.OvalBuilder;
import com.arpnetworking.steno.Logger;
import com.arpnetworking.steno.LoggerFactory;
import net.sf.oval.Validator;
import net.sf.oval.constraint.CheckWith;
import net.sf.oval.constraint.CheckWithCheck;
import net.sf.oval.constraint.NotNull;
import net.sf.oval.context.OValContext;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;

import java.io.Serial;
import java.time.Duration;

/**
 * A runnable wrapper for a {@code Consumer} that will continually poll
 * the consumer.
 *
 * @param  the type of the values in kafka {@code ConsumerRecords}
 *
 * @author Joey Jackson (jjackson at dropbox dot com)
 */
public class RunnableConsumerImpl implements RunnableConsumer {

    private static final Logger LOGGER = LoggerFactory.getLogger(RunnableConsumerImpl.class);

    private final ConsumerListener _listener;
    private final Consumer _consumer;
    private final Duration _pollTime;
    private volatile boolean _isRunning;

    /* package private */ RunnableConsumerImpl(final Builder builder) {
        _consumer = builder._consumer;
        _listener = builder._listener;
        _pollTime = builder._pollTime;
        _isRunning = true;
    }

    @Override
    public void run() {
        Thread.currentThread().setUncaughtExceptionHandler(
                (thread, throwable) -> LOGGER.error()
                        .setMessage("Unhandled exception")
                        .setThrowable(throwable)
                        .log());

        while (isRunning()) {
            try {
                final ConsumerRecords records = _consumer.poll(_pollTime);

                for (final ConsumerRecord record : records) {
                    _listener.handle(record);
                }
                _consumer.commitSync();
            // CHECKSTYLE.OFF: IllegalCatch - Allow clients to decide how to handle exceptions
            } catch (final Exception e) {
            // CHECKSTYLE.ON: IllegalCatch
                _listener.handle(e);
            }
        }
    }

    @Override
    public void stop() {
        _isRunning = false;
    }

    /**
     * Determine whether the consumer thread is running.
     *
     * @return true if running false if not running.
     */
    protected boolean isRunning() {
        return _isRunning;
    }

    /**
     * {@link com.arpnetworking.commons.builder.Builder} implementation for
     * {@link RunnableConsumerImpl}.
     *
     * @param  the type of the values pulled from kafka records
     *
     * @author Joey Jackson (jjackson at dropbox dot com)
     */
    public static final class Builder extends OvalBuilder> {

        /**
         * Public constructor.
         */
        public Builder() {
            super((java.util.function.Function,
                    RunnableConsumerImpl>) RunnableConsumerImpl::new);
        }

        /**
         * Sets the {@link ConsumerListener} instance. Cannot be null.
         *
         * @param listener The {@link ConsumerListener} instance.
         * @return This instance of {@link RunnableConsumerImpl.Builder}
         */
        public RunnableConsumerImpl.Builder setListener(final ConsumerListener listener) {
            _listener = listener;
            return this;
        }

        /**
         * Sets the {@code Consumer} instance. Cannot be null.
         *
         * @param consumer The {@code Consumer} instance.
         * @return This instance of {@link RunnableConsumerImpl.Builder}
         */
        public RunnableConsumerImpl.Builder setConsumer(final Consumer consumer) {
            _consumer = consumer;
            return this;
        }

        /**
         * Sets the duration the consumer will poll kafka for each consume. Cannot be null.
         *
         * @param pollTime The {@code Duration} instance.
         * @return This instance of {@link RunnableConsumerImpl.Builder}
         */
        public RunnableConsumerImpl.Builder setPollTime(final Duration pollTime) {
            _pollTime = pollTime;
            return this;
        }

        @NotNull
        private Consumer _consumer;
        @NotNull
        private ConsumerListener _listener;
        @NotNull
        @CheckWith(value = PositiveDuration.class, message = "Poll duration must be positive.")
        private Duration _pollTime;

        private static final class PositiveDuration implements CheckWithCheck.SimpleCheck {
            @Override
            public boolean isSatisfied(
                    final Object validatedObject,
                    final Object value,
                    final OValContext context,
                    final Validator validator) {
                return value instanceof Duration && !((Duration) value).isNegative();
            }

            @Serial
            private static final long serialVersionUID = 1L;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy