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

net.openhft.chronicle.wire.internal.extractor.DocumentExtractorBuilder Maven / Gradle / Ivy

There is a newer version: 2.27ea1
Show newest version
/*
 * Copyright 2016-2022 chronicle.software
 *
 *       https://chronicle.software
 *
 * 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 net.openhft.chronicle.wire.internal.extractor;

import net.openhft.chronicle.core.util.ThreadConfinementAsserter;
import net.openhft.chronicle.wire.domestic.extractor.DocumentExtractor;
import org.jetbrains.annotations.NotNull;

import java.util.function.BiConsumer;
import java.util.function.Supplier;

import static net.openhft.chronicle.core.util.ObjectUtils.requireNonNull;

/**
 * Responsible for building and configuring document extractors for a specified type {@code E}.
 * This class provides flexibility in the extraction process, enabling efficient document extraction.
 */
public final class DocumentExtractorBuilder implements DocumentExtractor.Builder {

    // Represents the type of element to be extracted.
    private final Class elementType;

    // A supplier to provide instances of the type E.
    private Supplier supplier;

    // Flag to determine if reuse of the supplier is confined to the current thread.
    private boolean threadConfinedReuse;

    // Reference to the method used for extraction.
    private MethodRef methodRef;

    /**
     * Constructs a new DocumentExtractorBuilder for the specified element type.
     *
     * @param elementType Class of the elements to be extracted.
     */
    public DocumentExtractorBuilder(@NotNull final Class elementType) {
        this.elementType = requireNonNull(elementType);
    }

    @NotNull
    @Override
    public DocumentExtractor.Builder withReusing(@NotNull Supplier supplier) {
        this.supplier = requireNonNull(supplier);
        return this;
    }

    @NotNull
    @Override
    public DocumentExtractor.Builder withThreadConfinedReuse() {
        threadConfinedReuse = true;
        return this;
    }

    @SuppressWarnings("unchecked")
    @NotNull
    @Override
    public  DocumentExtractor.Builder withMethod(@NotNull final Class interfaceType,
                                                       @NotNull final BiConsumer methodReference) {
        methodRef = (MethodRef) new MethodRef<>(interfaceType, methodReference);
        return this;
    }

    @NotNull
    @Override
    public DocumentExtractor build() {

        if (methodRef != null) {
            if (supplier == null) {
                // () -> null means null will be used as reuse meaning new objects are created
                return DocumentExtractorUtil.ofMethod(methodRef.interfaceType(), methodRef.methodReference(), () -> null);
            }
            return DocumentExtractorUtil.ofMethod(methodRef.interfaceType(), methodRef.methodReference(), guardedSupplier());
        }

        if (supplier == null) {
            return (wire, index) -> wire
                    .getValueIn()
                    .object(elementType); // No lambda capture
        } else {
            final Supplier internalSupplier = guardedSupplier();
            return (wire, index) -> {
                final E using = internalSupplier.get();
                return wire
                        .getValueIn()
                        .object(using, elementType); // Lambda capture
            };
        }
    }

    /**
     * Provides a thread-safe supplier, ensuring the supplier's reuse is either confined to the
     * current thread or utilizes a thread-local mechanism.
     *
     * @return A guarded supplier of type E.
     */
    Supplier guardedSupplier() {
        // Determines which supplier to use based on thread confinement.
        return threadConfinedReuse
                ? new ThreadConfinedSupplier<>(supplier)
                : new ThreadLocalSupplier<>(supplier);
    }

    /**
     * A supplier backed by a ThreadLocal instance, ensuring separate values for each thread.
     */
    static final class ThreadLocalSupplier implements Supplier {

        // ThreadLocal instance to provide thread-specific values.
        private final ThreadLocal threadLocal;

        /**
         * Constructs the ThreadLocalSupplier with a given supplier.
         *
         * @param supplier The supplier to initialize the thread-local with.
         */
        public ThreadLocalSupplier(@NotNull final Supplier supplier) {
            this.threadLocal = ThreadLocal.withInitial(supplier);
        }

        @Override
        public E get() {
            return threadLocal.get();
        }
    }

    /**
     * A supplier that ensures its use is confined to a single thread, providing thread safety.
     */
    static final class ThreadConfinedSupplier implements Supplier {

        // Utility to assert that the current thread is the one this supplier is confined to.
        private final ThreadConfinementAsserter asserter = ThreadConfinementAsserter.createEnabled();

        // The actual object to be supplied.
        private final E delegate;

        /**
         * Constructs the ThreadConfinedSupplier with a given supplier.
         *
         * @param supplier The supplier to provide the delegate object.
         */
        public ThreadConfinedSupplier(@NotNull final Supplier supplier) {
            // Eagerly create the reuse object
            this.delegate = requireNonNull(supplier.get());
        }

        @Override
        public E get() {
            asserter.assertThreadConfined();
            return delegate;
        }
    }

    /**
     * Represents a reference to a method for extracting data.
     */
    private static final class MethodRef {

        // Type of interface the method belongs to.
        final Class interfaceType;

        // Reference to the extraction method.
        final BiConsumer methodReference;

        /**
         * Constructs the MethodRef with specified interface type and method reference.
         *
         * @param interfaceType The class type of the interface.
         * @param methodReference The actual method reference for extraction.
         */
        @SuppressWarnings("unchecked")
        public MethodRef(@NotNull final Class interfaceType,
                         @NotNull final BiConsumer methodReference) {
            this.interfaceType = interfaceType;
            this.methodReference = (BiConsumer) methodReference;
        }

        public Class interfaceType() {
            return interfaceType;
        }

        public BiConsumer methodReference() {
            return methodReference;
        }
    }
}