001/**
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2019 nobark (tools4j), Marco Terzer, Anton Anufriev
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in all
014 * copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022 * SOFTWARE.
023 */
024package org.tools4j.nobark.queue;
025
026import java.util.function.Supplier;
027
028/**
029 * A listener for an {@link ConflationQueue.Poller poller} of a {@link ConflationQueue} for instance useful to
030 * record performance metrics related to poll operations.  The listener or a supplier thereof is usually passed to
031 * the constructor of a conflation queue.  Note that listeners must be thread safe if used in a multi-consumer
032 * environment;  best practice is to use separate listener instances for each consumer thread e.g. by using
033 * {@link #threadLocal(Supplier)} or {@link #threadLocalSupplier(Supplier)}.
034 *
035 * @param <K> the type of the conflation key
036 * @param <V> the type of values in the queue
037 */
038@FunctionalInterface
039public interface PollerListener<K,V> {
040
041    /**
042     * Called whenever a value is polled via any of the {@link ConflationQueue.Poller#poll() poll} methods.  By default
043     * this method is also invoked when the queue was found empty (with null key and value) unless
044     * {@link #polledButFoundEmpty(ConflationQueue)} is overridden and implemented differently.
045     *
046     * @param queue the conflation queue, sometimes useful to record queue size metrics, never null
047     * @param key   the conflation key, may be null for poll invocations of the empty queue
048     * @param value the value that was polled, or null for unsuccessful poll invocations (i.e. when the queue was found
049     *              empty)
050     */
051    void polled(ConflationQueue<? extends K, ? extends V> queue, K key, V value);
052
053    /**
054     * Called whenever a poll attempt was made but the queue was found empty.  The default implementation delegates the
055     * call to {@link #polled(ConflationQueue, Object, Object)} passing nulls for key and value.
056     *
057     * @param queue the conflation queue, sometimes useful to record queue size metrics, never null
058     */
059    default void polledButFoundEmpty(final ConflationQueue<? extends K, ? extends V> queue) {
060        polled(queue, null, null);
061    }
062
063    /**
064     * Constant for a no-op listener.
065     */
066    PollerListener<Object,Object> NOOP = (q, k, v) -> {};
067
068    /**
069     * Creates a poller listener that delegates to thread-local listener instances created on demand by the given
070     * supplier.  This listener can be passed to the constructors of {@link AtomicConflationQueue} which use a single
071     * poller instance for all consumer threads.
072     *
073     * @param listenerSupplier  the supplier acting as factory for per-thread listener instances
074     * @param <K> the type of the conflation key
075     * @param <V> the type of values in the queue
076     * @return a listener that delegates to thread-local listener instances
077     */
078    static <K,V> PollerListener<K,V> threadLocal(final Supplier<? extends PollerListener<K,V>> listenerSupplier) {
079        final ThreadLocal<PollerListener<K,V>> threadLocal = ThreadLocal.withInitial(listenerSupplier);
080        return (q,k,v) -> threadLocal.get().polled(q,k,v);
081    }
082
083    /**
084     * Creates a thread local supplier that creates a new instance once for every caller thread.  This listener can be
085     * passed to the constructors of {@link EvictConflationQueue} and {@link MergeConflationQueue} which use a per
086     * thread poller instances;  the returned supplier will create a new thread-local listener instance for every
087     * poller instance that is created.
088     *
089     * @param listenerSupplier  the supplier acting as factory for per-thread listener instances
090     * @param <L> the poller listener (sub-)type
091     * @return a supplier creating thread-local listener instances
092     */
093    static <L extends PollerListener<?,?>> Supplier<L> threadLocalSupplier(final Supplier<L> listenerSupplier) {
094        return ThreadLocal.withInitial(listenerSupplier)::get;
095    }
096}