com.tangosol.internal.util.stream.AbstractPipeline Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* 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 extends K> 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 extends InvocableMap.Entry extends K, ? extends V>> stream)
{
AbstractPipeline, ?, ?, E_IN, ?, S_IN> 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 super E_OUT> 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 super E_OUT> 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 super K, ? super V, R> 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 extends K> 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 super K, ? super V, R> 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 extends K> 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 extends K> 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 super E_OUT> 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;
}