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

com.tangosol.internal.util.stream.AbstractPipeline Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * https://oss.oracle.com/licenses/upl.
 */
package com.tangosol.internal.util.stream;

import com.tangosol.io.ExternalizableLite;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;

import com.tangosol.io.pof.PortableObject;
import com.tangosol.net.NamedCache;
import com.tangosol.net.PartitionedService;
import com.tangosol.net.cache.ContinuousQueryCache;
import com.tangosol.util.ExternalizableHelper;
import com.tangosol.util.Filter;
import com.tangosol.util.InvocableMap;

import com.tangosol.util.stream.BaseRemoteStream;
import com.tangosol.util.stream.RemoteCollector;
import com.tangosol.util.stream.RemoteCollectors;
import com.tangosol.util.stream.RemotePipeline;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import java.util.Collection;

import java.util.Comparator;
import java.util.Set;
import java.util.function.Function;

import java.util.stream.BaseStream;
import java.util.stream.Stream;

import jakarta.json.bind.annotation.JsonbProperty;

/**
 * Abstract base class for stream pipeline implementations.
 *
 * @author as  2014.08.26
 */
public abstract class AbstractPipeline, S_OUT extends BaseStream>
        implements BaseRemoteStream, RemotePipeline,
                   ExternalizableLite, PortableObject
    {
    // ---- constructors ----------------------------------------------------

    /**
     * Deserialization constructor.
     */
    protected AbstractPipeline()
        {
        }

    /**
     * Constructor for the head of a stream pipeline.
     *
     * @param map             the stream source
     * @param fParallel       true if the pipeline is parallel
     */
    protected AbstractPipeline(InvocableMap map, boolean fParallel,
                               Collection colKeys, Filter filter,
                               Function intermediateOp)
        {
        m_invoker        = new AggregatorInvoker<>(map, colKeys, filter);
        m_previousStage  = null;
        m_fParallel      = fParallel;
        m_intermediateOp = intermediateOp;
        }

    /**
     * Constructor for appending an intermediate operation stage onto an
     * existing pipeline.
     *
     * @param previousStage   the upstream pipeline stage
     * @param intermediateOp  intermediate operation for this stage
     */
    protected AbstractPipeline(AbstractPipeline previousStage,
                               Function intermediateOp)
        {
        if (previousStage.m_fLinkedOrConsumed)
            {
            throw new IllegalStateException(MSG_STREAM_LINKED);
            }
        previousStage.m_fLinkedOrConsumed = true;

        m_previousStage = previousStage;
        m_intermediateOp = intermediateOp;
        }

    // ---- Pipeline interface ----------------------------------------------

    public  S_OUT evaluate(Stream> stream)
        {
        AbstractPipeline previousStage = m_previousStage;
        return previousStage == null
               ? ((Function>, S_OUT>) m_intermediateOp).apply(stream)
               : m_intermediateOp.apply(previousStage.evaluate(stream));
        }

    // ---- BaseStream interface --------------------------------------------

    public void close()
        {
        m_fLinkedOrConsumed = true;
        m_invoker = null;
        if (head().m_sourceCloseAction != null)
            {
            Runnable closeAction = head().m_sourceCloseAction;
            head().m_sourceCloseAction = null;
            closeAction.run();
            }
        }

    @SuppressWarnings("unchecked")
    public S_OUT onClose(Runnable closeHandler)
        {
        Runnable existingHandler = head().m_sourceCloseAction;
        head().m_sourceCloseAction =
                (existingHandler == null)
                ? closeHandler
                : composeWithExceptions(existingHandler, closeHandler);
        return (S_OUT) this;
        }

    public RemotePipeline pipeline()
        {
        return this;
        }

    public final boolean isParallel()
        {
        return head().m_fParallel;
        }

    // ---- accessors -------------------------------------------------------

    /**
     * Set a flag specifying whether this stream is parallel or sequential.
     *
     * @param fParallel  true if parallel, false if sequential
     */
    protected void setParallel(boolean fParallel)
        {
        head().m_fParallel = fParallel;
        }

    /**
     * Return a comparator that should be used to sort stream elements.
     *
     * @return  a comparator to use
     */
    public Comparator getComparator()
        {
        return m_comparator;
        }

    /**
     * Set a comparator that should be used to sort stream elements.
     *
     * @param comparator  a comparator to use
     */
    protected void setComparator(Comparator comparator)
        {
        m_comparator = comparator;
        }

    /**
     * Return true if this stream has sort order defined.
     *
     * @return  true if this stream has sort order defined
     */
    protected boolean isSorted()
        {
        boolean          fSorted = false;
        AbstractPipeline curr    = this;
        while (curr.m_previousStage != null && !fSorted)
            {
            fSorted = curr.m_comparator != null;
            curr    = curr.m_previousStage;
            }
        return fSorted;
        }

    /**
     * Return the InvocableMap this stream was created from.
     *
     * @return  the InvocableMap this stream was created from
     */
    protected InvocableMap getMap()
        {
        return head().m_invoker.getMap();
        }

    /**
     * Determine whether or not the iteration of the underlying map for this
     * stream could be partitioned.
     *
     * @return  true if the iteration can be partitioned, false otherwise
     */
    protected boolean isPartitionable()
        {
        if (getInvoker().getKeys() != null)
            {
            // theoretically we could partition key set-based stream as well
            // using InKeySetFilter, but there is very little benefit in doing
            // so since the key set-based aggregation scales better
            return false;
            }

        InvocableMap map = getMap();
        return map instanceof NamedCache &&
               !(map instanceof ContinuousQueryCache && ((ContinuousQueryCache) map).isCacheValues()) &&
               ((NamedCache) map).getCacheService() instanceof PartitionedService;
        }

    /**
     * Invoke the aggregator.
     *
     * @param aggregator  aggregator to invoke
     * @param          the type of aggregation result
     *
     * @return  the aggregation result
     */
    protected  R invoke(InvocableMap.EntryAggregator aggregator)
        {
        return head().m_invoker.invoke(aggregator);
        }

    public AggregatorInvoker getInvoker()
        {
        return head().m_invoker;
        }

    /**
     * Return the head of the pipeline.
     *
     * @return  the head of the pipeline
     */
    protected AbstractPipeline head()
        {
        AbstractPipeline head = this;
        while (head.m_previousStage != null)
            {
            head = head.m_previousStage;
            }
        return head;
        }

    // ---- helpers ---------------------------------------------------------

    /**
     * Return appropriate collection collector based on whether this stream is
     * sorted or not.
     *
     * @return  a collector that collects stream elements into a Collection
     */
    protected RemoteCollector> toCollection()
        {
        return isSorted()
               ? RemoteCollectors.toSortedBag(getComparator())
               : RemoteCollectors.toList();
        }

    /**
     * Return appropriate set collector based on whether this stream is
     * sorted or not.
     *
     * @return  a collector that collects stream elements into a Set
     */
    protected RemoteCollector> toSet()
        {
        return isSorted()
               ? RemoteCollectors.toSortedSet(getComparator())
               : RemoteCollectors.toSet();
        }

    /**
     * Given two Runnables, return a Runnable that executes both in sequence,
     * even if the first throws an exception, and if both throw exceptions, add
     * any exceptions thrown by the second as suppressed exceptions of the
     * first.
     *
     * @param a  first runnable to execute
     * @param b  second runnable to execute
     */
    private Runnable composeWithExceptions(Runnable a, Runnable b)
        {
        return new Runnable()
            {
            @Override
            public void run()
                {
                try
                    {
                    a.run();
                    }
                catch (Throwable e1)
                    {
                    try
                        {
                        b.run();
                        }
                    catch (Throwable e2)
                        {
                        try
                            {
                            e1.addSuppressed(e2);
                            }
                        catch (Throwable ignore)
                            {
                            }
                        }
                    throw e1;
                    }
                b.run();
                }
            };
        }

    // ---- ExternalizableLite interface ------------------------------------

    public void readExternal(DataInput in) throws IOException
        {
        m_fParallel      = in.readBoolean();
        m_comparator     = ExternalizableHelper.readObject(in);
        m_previousStage  = ExternalizableHelper.readObject(in);
        m_intermediateOp = ExternalizableHelper.readObject(in);
        }

    public void writeExternal(DataOutput out) throws IOException
        {
        out.writeBoolean(m_fParallel);
        ExternalizableHelper.writeObject(out, m_comparator);
        ExternalizableHelper.writeObject(out, m_previousStage);
        ExternalizableHelper.writeObject(out, m_intermediateOp);
        }

    // ---- PortableObject interface ----------------------------------------

    public void readExternal(PofReader reader) throws IOException
        {
        m_fParallel      = reader.readBoolean(0);
        m_comparator     = reader.readObject(1);
        m_previousStage  = reader.readObject(2);
        m_intermediateOp = reader.readObject(3);
        }

    public void writeExternal(PofWriter writer) throws IOException
        {
        writer.writeBoolean(0, m_fParallel);
        writer.writeObject(1, m_comparator);
        writer.writeObject(2, m_previousStage);
        writer.writeObject(3, m_intermediateOp);
        }

    // ---- inner class: AggregatorInvoker ----------------------------------

    /**
     * A helper class that invokes aggregator on either key set or a filter,
     * depending on which one is specified.
     *
     * @param   the key type
     * @param   the value type
     */
    protected static class AggregatorInvoker
        {
        /**
         * Construct AggregatorInvoker instance.
         *
         * @param colKeys  the key set to aggregate on
         * @param filter   the filter to aggregate on
         */
        public AggregatorInvoker(InvocableMap map, Collection colKeys, Filter filter)
            {
            m_map     = map;
            m_colKeys = colKeys;
            m_filter  = filter;
            }

        /**
         * Invoke specified aggregator and return the result.
         *
         * @param aggregator  the aggregator to invoke
         * @param          the type of the result
         *
         * @return the aggregation result
         */
        public  R invoke(InvocableMap.EntryAggregator aggregator)
            {
            return m_colKeys == null
                   ? getMap().aggregate(m_filter, aggregator)
                   : getMap().aggregate(m_colKeys, aggregator);
            }

        /**
         * Return the InvocableMap this invoker is for.
         *
         * @return  the InvocableMap this invoker is for
         */
        public InvocableMap getMap()
            {
            InvocableMap map = m_map;
            if (map == null)
                {
                throw new IllegalStateException("Cannot invoke terminal operation on a pipeline builder");
                }

            return map;
            }

        public Collection getKeys()
            {
            return m_colKeys;
            }

        public Filter getFilter()
            {
            return m_filter;
            }

        // ---- data members ------------------------------------------------

        /**
         * The InvocableMap to aggregate.
         */
        private InvocableMap m_map;

        /**
         * The key set to aggregate on.
         */
        private Collection m_colKeys;

        /**
         * The filter to aggregate on.
         */
        private Filter m_filter;
        }

    // ---- data members ----------------------------------------------------

    private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed";

    /**
     * The aggregator invoker.
     */
    private transient AggregatorInvoker m_invoker;

    /**
     * True if this pipeline has been linked or consumed
     */
    private transient boolean m_fLinkedOrConsumed;

    /**
     * An action to run when the source is closed
     */
    private transient Runnable m_sourceCloseAction;

    /**
     * True if pipeline is parallel, otherwise the pipeline is sequential; only
     * valid for the source stage.
     */
    @JsonbProperty("isParallel")
    private boolean m_fParallel;

    /**
     * A comparator to use if the pipeline is sorted.
     */
    @JsonbProperty("comparator")
    private Comparator m_comparator;

    /**
     * The "upstream" pipeline, or null if this is the source stage.
     */
    @JsonbProperty("previousStage")
    private AbstractPipeline m_previousStage;

    /**
     * Intermediate operation performed by this pipeline stage.
     */
    @JsonbProperty("intermediateOp")
    private Function m_intermediateOp;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy