branched for releases 2.0.x (first created for gCube 2.10.0)
git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/branches/data-access/streams/2.0@57745 82a268e6-3cf1-43bd-a215-b396298e98cfmaster
commit
83641cc478
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>streams</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,6 @@
|
||||
gCube System - License
|
||||
------------------------------------------------------------
|
||||
|
||||
The gCube/gCore software is licensed as Free Open Source software conveying to the EUPL (http://ec.europa.eu/idabc/eupl).
|
||||
The software and documentation is provided by its authors/distributors "as is" and no expressed or
|
||||
implied warranty is given for its use, quality or fitness for a particular case.
|
@ -0,0 +1 @@
|
||||
* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy
|
@ -0,0 +1,38 @@
|
||||
The gCube System - ${name}
|
||||
----------------------
|
||||
|
||||
This work has been partially supported by the following European projects: DILIGENT (FP6-2003-IST-2), D4Science (FP7-INFRA-2007-1.2.2),
|
||||
D4Science-II (FP7-INFRA-2008-1.2.2), iMarine (FP7-INFRASTRUCTURES-2011-2), and EUBrazilOpenBio (FP7-ICT-2011-EU-Brazil).
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy.
|
||||
|
||||
Version and Release Date
|
||||
------------------------
|
||||
${version}
|
||||
|
||||
Description
|
||||
-----------
|
||||
${description}
|
||||
|
||||
Download information
|
||||
--------------------
|
||||
|
||||
Source code is available from SVN:
|
||||
${scm.url}
|
||||
|
||||
Binaries can be downloaded from:
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Documentation is available on-line from the Projects Documentation Wiki:
|
||||
https://gcube.wiki.gcube-system.org/gcube/index.php/The_Streams_Library
|
||||
|
||||
|
||||
Licensing
|
||||
---------
|
||||
|
||||
This software is licensed under the terms you may find in the file named "LICENSE" in this directory.
|
@ -0,0 +1,30 @@
|
||||
<ReleaseNotes>
|
||||
<Changeset component="${build.finalName}" date="2012-04-26">
|
||||
<Change>First Release</Change>
|
||||
</Changeset>
|
||||
<Changeset component="${build.finalName}" date="2012-09-01">
|
||||
<Change>
|
||||
Fault handling in stream publication and consumption aligns
|
||||
with fault transport facilities of latest gRS2.
|
||||
</Change>
|
||||
<Change>Faults are published and abort publication unless they are
|
||||
contingencies (cf. StreamContingency annotation).
|
||||
</Change>
|
||||
<Change>Streams can be conveniently consumed with callbacks (cf.
|
||||
Callback interface and Streams#consume()) and callbacks can explicitly skip
|
||||
elements or stop
|
||||
consumption (cf. Iteration class).
|
||||
</Change>
|
||||
<Change>
|
||||
Like callbacks, fault handlers can control iteration directly (cf. Iteration). FaultResponse is no longer needed.
|
||||
</Change>
|
||||
<Change>
|
||||
StreamSkipException is replaced internally with
|
||||
StreamSkipSignal, and StreamStopSignal is also added to model signals from callbacks and fault handlers.
|
||||
</Change>
|
||||
<Change>
|
||||
New stream implementations as well as code that consumes
|
||||
streams can now be tested with dedicated facilities.
|
||||
</Change>
|
||||
</Changeset>
|
||||
</ReleaseNotes>
|
@ -0,0 +1,42 @@
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>servicearchive</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<baseDirectory>/</baseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${distroDirectory}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<includes>
|
||||
<include>README</include>
|
||||
<include>LICENSE</include>
|
||||
<include>INSTALL</include>
|
||||
<include>MAINTAINERS</include>
|
||||
<include>changelog.xml</include>
|
||||
</includes>
|
||||
<fileMode>755</fileMode>
|
||||
<filtered>true</filtered>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<files>
|
||||
<file>
|
||||
<source>${distroDirectory}/profile.xml</source>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
<file>
|
||||
<source>target/${build.finalName}.jar</source>
|
||||
<outputDirectory>/${artifactId}</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${distroDirectory}/svnpath.txt</source>
|
||||
<outputDirectory>/${artifactId}</outputDirectory>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ID />
|
||||
<Type>Service</Type>
|
||||
<Profile>
|
||||
<Description>${description}</Description>
|
||||
<Class>DataAccess</Class>
|
||||
<Name>${artifactId}</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Packages>
|
||||
<Software>
|
||||
<Name>${artifactId}</Name>
|
||||
<Version>${version}</Version>
|
||||
<MavenCoordinates>
|
||||
<groupId>${groupId}</groupId>
|
||||
<artifactId>${artifactId}</artifactId>
|
||||
<version>${version}</version>
|
||||
</MavenCoordinates>
|
||||
<Files>
|
||||
<File>${build.finalName}.jar</File>
|
||||
</Files>
|
||||
</Software>
|
||||
</Packages>
|
||||
</Profile>
|
||||
</Resource>
|
||||
|
@ -0,0 +1 @@
|
||||
${scm.url}
|
@ -0,0 +1,120 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>maven-parent</artifactId>
|
||||
<groupId>org.gcube.tools</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.gcube.data.access</groupId>
|
||||
<artifactId>streams</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
|
||||
|
||||
<name>Stream Library</name>
|
||||
<description>Embedded Domain-Specific Language for Stream Transformations</description>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/data-access/${project.artifactId}</connection>
|
||||
<developerConnection>scm:svn:https://svn.d4science.research-infrastructures.eu/gcube/trunk/data-access/${project.artifactId}</developerConnection>
|
||||
<url>http://svn.d4science.research-infrastructures.eu/gcube/trunk/data-access/${project.artifactId}</url>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<distroDirectory>distro</distroDirectory>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.execution</groupId>
|
||||
<artifactId>grs2library</artifactId>
|
||||
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.6.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-profile</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${distroDirectory}</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>profile.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>${distroDirectory}/descriptor.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>servicearchive</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,23 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
|
||||
/**
|
||||
* A callback for a {@link StreamConsumer}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <T> the type of stream elements
|
||||
*
|
||||
*/
|
||||
public interface Callback<T> {
|
||||
|
||||
/** The ongoing iteration. */
|
||||
static final Iteration iteration = new Iteration();
|
||||
|
||||
/**
|
||||
* Implement to consume an element of the stream.
|
||||
* @param element the element
|
||||
*/
|
||||
void consume(T element);
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
|
||||
/**
|
||||
* A model of a {@link Stream} iteration, with facilities to control it from within {@link Callback}s and {@link FaultHandler}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public final class Iteration {
|
||||
|
||||
/**
|
||||
* Stops the ongoing iteration.
|
||||
*/
|
||||
public void stop() throws StreamStopSignal {
|
||||
throw new StreamStopSignal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip this element of the ongoing iteration.
|
||||
*/
|
||||
public void skip() throws StreamSkipSignal {
|
||||
throw new StreamSkipSignal();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
import org.gcube.data.streams.handlers.RethrowHandler;
|
||||
|
||||
/**
|
||||
* Partial {@link Stream} implementation based on look-ahead operations over an underlying stream.
|
||||
* <p>
|
||||
* The implementation attempts to prefetch the output of {@link #next()} in {@link #hasNext()}. If a failure occurs,
|
||||
* {@link #hasNext()}:
|
||||
*
|
||||
* <li>keeps consuming the underlying stream as long as the failure is a {@link StreamSkipSignal};
|
||||
* <li>consults a {@link FaultHandler} for all the other failures. If the {@link FaultHandler} re-throws the same or a
|
||||
* different exception, the implementation throws it at the following {@link #next()}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public abstract class LookAheadStream<E> implements Stream<E> {
|
||||
|
||||
private FaultHandler handler = new RethrowHandler();
|
||||
|
||||
// iteration state
|
||||
protected Boolean hasNext;
|
||||
protected E element;
|
||||
private RuntimeException failure;
|
||||
|
||||
/**
|
||||
* Sets the {@link FaultHandler} for the iteration
|
||||
*
|
||||
* @param handler the handler
|
||||
* @throws IllegalArgumentException if the handler is <code>null</code>
|
||||
*/
|
||||
public void setHandler(FaultHandler handler) throws IllegalArgumentException {
|
||||
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("invalid null handler");
|
||||
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
|
||||
if (hasNext==null)
|
||||
hasNext = lookAhead();
|
||||
|
||||
//auto-close
|
||||
if (!hasNext)
|
||||
close();
|
||||
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
private boolean lookAhead() {
|
||||
|
||||
if (!delegateHasNext())
|
||||
return false;
|
||||
|
||||
try {
|
||||
this.element = delegateNext();
|
||||
return true;
|
||||
}
|
||||
catch (RuntimeException failure) {
|
||||
|
||||
try {
|
||||
handler.handle(failure);
|
||||
return lookAhead();
|
||||
}
|
||||
catch(StreamSkipSignal skip) {
|
||||
return lookAhead();
|
||||
}
|
||||
catch(StreamStopSignal stop) {
|
||||
return false;
|
||||
}
|
||||
catch(RuntimeException rethrownUnchecked) {
|
||||
this.failure=rethrownUnchecked;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final E next() {
|
||||
|
||||
try {
|
||||
throwLookAheadFailureIfAny();
|
||||
return lookedAheadElementOrGetItNow();
|
||||
}
|
||||
finally {
|
||||
cleanIterationState();
|
||||
}
|
||||
}
|
||||
|
||||
private void throwLookAheadFailureIfAny() {
|
||||
if (failure != null)
|
||||
throw failure;
|
||||
}
|
||||
|
||||
private E lookedAheadElementOrGetItNow() {
|
||||
|
||||
if (element == null)
|
||||
if (hasNext())
|
||||
return next();
|
||||
else
|
||||
throw new NoSuchElementException();
|
||||
|
||||
return element;
|
||||
|
||||
}
|
||||
|
||||
private void cleanIterationState() {
|
||||
failure=null;
|
||||
element=null;
|
||||
hasNext = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns an element of the underlying stream
|
||||
*
|
||||
* @return the element
|
||||
*/
|
||||
protected abstract E delegateNext();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the underlying stream has more elements.
|
||||
*
|
||||
* @return {@code true} if the underlying stream has more elements
|
||||
*/
|
||||
protected abstract boolean delegateHasNext();
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.gcube.data.streams.adapters.IteratorStream;
|
||||
import org.gcube.data.streams.adapters.ResultsetStream;
|
||||
import org.gcube.data.streams.delegates.FoldedStream;
|
||||
import org.gcube.data.streams.delegates.GuardedStream;
|
||||
import org.gcube.data.streams.delegates.MonitoredStream;
|
||||
import org.gcube.data.streams.delegates.PipedStream;
|
||||
import org.gcube.data.streams.delegates.UnfoldedStream;
|
||||
import org.gcube.data.streams.dsl.Faults;
|
||||
import org.gcube.data.streams.dsl.Streams;
|
||||
import org.gcube.data.streams.exceptions.StreamOpenException;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
import org.gcube.data.streams.publishers.RsPublisher;
|
||||
import org.gcube.data.streams.publishers.StreamPublisher;
|
||||
|
||||
/**
|
||||
* An {@link Iterator} over the elements of a dataset of arbitrary origin, including memory, secondary storage, and
|
||||
* network.
|
||||
* <p>
|
||||
*
|
||||
* <h3>Properties</h3><br>
|
||||
*
|
||||
* Streams are:
|
||||
* <p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><em>addressable</em>: clients may invoke {@link #locator()} to obtain a reference to their address. The use and
|
||||
* syntax of locators is implementation-dependent.
|
||||
* <li><em>closeable</em>: clients may invoke {@link #close()} to allow implementations to release resources. Clients
|
||||
* <em>should</em> invoke {@link #close()} if they do not consume streams in their entirety. Implementations
|
||||
* <em>must</em> automatically release their resources when they have been consumed in their entirety.
|
||||
* <li><em>fallible</em>: invoking {@link #next()} over streams that originate from secondary storage and remote
|
||||
* locations may raise a wide range failures. Some failures may be <em>recoverable</em>, in that subsequent invocations
|
||||
* of {@link #next()} <em>may</em> still succeed. Other failures may be <em>unrecoverable</em>, in that subsequent
|
||||
* invocations of {@link #next()} are guaranteed to fail too.
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Implementations</h3><br>
|
||||
*
|
||||
* There are predefined implementations that adapt the {@link Stream} interface to existing {@link Iterator}s and remote
|
||||
* gRS2 resultsets (cf. {@link IteratorStream} and {@link ResultsetStream}).
|
||||
* <p>
|
||||
*
|
||||
* Other predefined implementations transform, fold, and unfold the elements of existing streams (cf.
|
||||
* {@link PipedStream}, {@link FoldedStream}, {@link UnfoldedStream}).
|
||||
* <p>
|
||||
*
|
||||
* Additional implementations allow modular handling of stream faults and notify interested listeners of stream
|
||||
* iteration events (cf. {@link GuardedStream}, {@link MonitoredStream}).
|
||||
* <p>
|
||||
*
|
||||
* Finally, streams may be published outside the current runtime by implementations of the {@link StreamPublisher}
|
||||
* interface. A predefined implementation supports publication of streams as gRS2 resultsets (cf. {@link RsPublisher}).
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* All the available implementations can be fluently instantiated and configured with an embedded DSL (cf.
|
||||
* {@link Streams}).
|
||||
*
|
||||
* <h3>Fault Handling</h3><br>
|
||||
*
|
||||
* Clients can implement {@link FaultHandler}s to specify fault handling policies over streams, and then wrap streams in
|
||||
* {@link GuardedStream}s that apply they policies:
|
||||
*
|
||||
* <pre>
|
||||
* import static ....Streams.*;
|
||||
* ...
|
||||
* Stream<T> stream = ...
|
||||
*
|
||||
* FaultHandler handler = new FaultHandler() {
|
||||
* public void handle(RuntimeException fault) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* Stream<T> guarded = guard(stream).with(handler);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* {@link FaultHandler}s can ignore faults, rethrow them, rethrow different faults, or use the constant
|
||||
* {@link FaultHandler#iteration} to stop the iteration of the underlying stream (cf. {@link Iteration#stop()})
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* Faults are unchecked exceptions thrown by {@link #next()}, often wrappers around an original cause.
|
||||
* {@link FaultHandler}s can use a fluent API to simplify the task of analysing fault causes (cf. {@link Faults}):
|
||||
*
|
||||
* <pre>
|
||||
* FaultHandler handler = new FaultHandler() {
|
||||
* public void handle(RuntimeException fault) {
|
||||
* try {
|
||||
* throw causeOf(fault).as(SomeException.class,SomeOtherException.class);
|
||||
* }
|
||||
* catch(SomeException e) {...}
|
||||
* catch(SomeOtherException e) {...}
|
||||
* }
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Consumption</h3><br>
|
||||
*
|
||||
* Clients may consume streams by explicitly iterating over their elements. Since streams are fallible and closeable,
|
||||
* the recommended idiom is the following:
|
||||
*
|
||||
* <pre>
|
||||
* Stream<T> stream = ...
|
||||
* try {
|
||||
* while (stream.hasNext())
|
||||
* ....stream.next()...
|
||||
* }
|
||||
* finally {
|
||||
* stream.close();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Alternatively, clients may provide {@link Callback}s to generic {@link StreamConsumer}s that iterate on
|
||||
* behalf of clients. Using the simplifications of the DSL:
|
||||
*
|
||||
* <pre>
|
||||
* Stream<T> stream = ...
|
||||
*
|
||||
* Callback<T> callback = new Callback<T>() {
|
||||
* public void consume(T element) {
|
||||
* ...element...
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* consume(stream).with(callback);
|
||||
* </pre>
|
||||
*
|
||||
* {@link Callback}s can control iteration through the {@link Iteration} constant (cf. {@link Callback#iteration}):
|
||||
*
|
||||
* <pre>
|
||||
* Callback<T> callback = new Callback<T>() {
|
||||
* public void consume(T element) {
|
||||
* ...iteration.stop()...
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param <E> the type of elements iterated over
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface Stream<E> extends Iterator<E> {
|
||||
|
||||
boolean hasNext();
|
||||
|
||||
/**
|
||||
* @throws NoSuchElementException if the stream has no more elements or it has been closed
|
||||
* @throws StreamOpenException if the stream cannot be opened
|
||||
* @throws RuntimeException if the element cannot be returned
|
||||
*/
|
||||
E next();
|
||||
|
||||
/**
|
||||
* Returns the stream locator.
|
||||
*
|
||||
* @return the locator
|
||||
* @throws IllegalStateException if the stream is no longer addressable at the time of invocation.
|
||||
*/
|
||||
URI locator();
|
||||
|
||||
/**
|
||||
* Closes the stream unconditionally, releasing any resources that it may be using.
|
||||
* <p>
|
||||
* Subsequent invocations of this method have no effect.<br>
|
||||
* Subsequents invocations of {@link #hasNext()} return {@code false}.<br>
|
||||
* Subsequent invocations of {@link #next()} throw {@link NoSuchElementException}s.
|
||||
* <p>
|
||||
* Failures are logged by implementations and suppressed otherwise.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the stream has been closed.
|
||||
*
|
||||
* @return <code>true</code> if the stream has been closed
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
|
||||
/**
|
||||
* A generic {@link Stream} consumer that delegates element processing and failure handling to a {@link Callback}.
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <T> the type of stream elements
|
||||
*/
|
||||
public final class StreamConsumer<T> {
|
||||
|
||||
private final Stream<T> stream;
|
||||
private final Callback<T> callback;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and a {@link Callback}
|
||||
* @param stream the stream
|
||||
* @param callback the callback
|
||||
*/
|
||||
public StreamConsumer(Stream<T> stream, Callback<T> callback) {
|
||||
this.stream=stream;
|
||||
this.callback=callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the iteration.
|
||||
*/
|
||||
public void start() {
|
||||
consume();
|
||||
}
|
||||
|
||||
//helper
|
||||
private void consume() {
|
||||
|
||||
try {
|
||||
consuming: while (stream.hasNext()) {
|
||||
|
||||
T next = stream.next();
|
||||
|
||||
try {
|
||||
callback.consume(next);
|
||||
}
|
||||
catch(StreamSkipSignal skip) {
|
||||
continue consuming;
|
||||
}
|
||||
catch(StreamStopSignal stop) {
|
||||
break consuming;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import gr.uoa.di.madgik.commons.server.PortRange;
|
||||
import gr.uoa.di.madgik.commons.server.TCPConnectionManager;
|
||||
import gr.uoa.di.madgik.commons.server.TCPConnectionManagerConfig;
|
||||
import gr.uoa.di.madgik.grs.proxy.tcp.TCPConnectionHandler;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamContingency;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Library utils.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(Utils.class);
|
||||
|
||||
/**
|
||||
* Initialises gRS2 library.
|
||||
*/
|
||||
public static synchronized void initialiseRS() {
|
||||
|
||||
if (TCPConnectionManager.IsInitialized())
|
||||
return;
|
||||
|
||||
log.info("gRS2 is not initialised: using defaults");
|
||||
|
||||
String host =null;
|
||||
try {
|
||||
host = InetAddress.getLocalHost().getHostName();
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.info("could not discover hostname, using 'localhost' to allow offline usage");
|
||||
host="localhost";
|
||||
}
|
||||
|
||||
try {
|
||||
TCPConnectionManager.Init(new TCPConnectionManagerConfig(host,new ArrayList<PortRange>(),true));
|
||||
TCPConnectionManager.RegisterEntry(new TCPConnectionHandler());
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a failure or its indirect causes are annotated with {@link StreamContingency}.
|
||||
* @param t the failure
|
||||
* @return <code>true</code> if the failure or its indirect causes are annotated with {@link StreamContingency}.
|
||||
*/
|
||||
public static boolean isContingency(Throwable t) {
|
||||
return t.getClass().isAnnotationPresent(StreamContingency.class)
|
||||
|| ((t.getCause()!=null) &&(isContingency(t.getCause())));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package org.gcube.data.streams.adapters;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* An adaptation strategy for {@link IteratorStream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
* @see IteratorStream
|
||||
*
|
||||
*/
|
||||
public class IteratorAdapter<E> implements Closeable {
|
||||
|
||||
private final Iterator<E> iterator;
|
||||
|
||||
/**
|
||||
* Creates an instance from a given {@link Iterator}.
|
||||
* @param iterator the iterator
|
||||
*/
|
||||
public IteratorAdapter(Iterator<E> iterator) {
|
||||
this.iterator=iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying iterator.
|
||||
* @return the iterator
|
||||
*/
|
||||
final Iterator<E> iterator() {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a locator for the underlying iterator.
|
||||
* <p>
|
||||
* By default it returns a locator of the form {@code local:<toString()>}, where {@code <toString()>} is the string
|
||||
* obtained by invoking {@link #toString()} on the iterator.
|
||||
* @return the locator
|
||||
*/
|
||||
public URI locator() {
|
||||
return URI.create("local://"+iterator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying iterator.
|
||||
* <p>
|
||||
* By defaults it has no effect except when the iterator implements the {@link Closeable} interface. In this
|
||||
* case, it simply delegates to the iterator.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (iterator instanceof Closeable)
|
||||
((Closeable) iterator).close();
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package org.gcube.data.streams.adapters;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A {@link Stream} adapter for {@link Iterator}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class IteratorStream<E> implements Stream<E> {
|
||||
|
||||
private static Logger log =LoggerFactory.getLogger(IteratorStream.class);
|
||||
|
||||
private final Iterator<E> iterator;
|
||||
private boolean closed;
|
||||
private final IteratorAdapter<E> adapter;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance that adapts a given {@link IteratorAdapter}.
|
||||
* @param adapter the adapter
|
||||
*/
|
||||
public IteratorStream(IteratorAdapter<E> adapter) {
|
||||
this.iterator=adapter.iterator();
|
||||
this.adapter=adapter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance that adapts a given {@link Iterator} with a default {@link IteratorAdapter}.
|
||||
* @param iterator the iterator
|
||||
*/
|
||||
public IteratorStream(final Iterator<E> iterator) {
|
||||
this(new IteratorAdapter<E>(iterator)); //use default adapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
|
||||
if (closed)
|
||||
return false; //respect close semantics
|
||||
|
||||
else {
|
||||
|
||||
boolean hasNext = iterator.hasNext();
|
||||
|
||||
if (!hasNext)
|
||||
close();
|
||||
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
|
||||
//respect close semantics
|
||||
if (closed)
|
||||
throw new NoSuchElementException();
|
||||
|
||||
return iterator.next();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
try {
|
||||
adapter.close();
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.error("could not close iterator "+locator(),e);
|
||||
}
|
||||
finally {
|
||||
closed=true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI locator() {
|
||||
return adapter.locator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package org.gcube.data.streams.adapters;
|
||||
|
||||
import gr.uoa.di.madgik.grs.buffer.IBuffer.Status;
|
||||
import gr.uoa.di.madgik.grs.reader.ForwardReader;
|
||||
import gr.uoa.di.madgik.grs.reader.GRS2ReaderException;
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
import gr.uoa.di.madgik.grs.record.exception.GRS2UncheckedException;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.gcube.data.streams.LookAheadStream;
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.exceptions.StreamException;
|
||||
import org.gcube.data.streams.exceptions.StreamOpenException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Stream} adapter for gRS2 resultsets.
|
||||
* <p>
|
||||
* This implementation is not thread safe.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ResultsetStream<E extends Record> extends LookAheadStream<E> {
|
||||
|
||||
private static Logger log =LoggerFactory.getLogger(ResultsetStream.class);
|
||||
|
||||
public static final int default_timeout = 30;
|
||||
public static final TimeUnit default_timeout_unit=TimeUnit.SECONDS;
|
||||
|
||||
private final URI locator;
|
||||
|
||||
private long timeout = default_timeout;
|
||||
private TimeUnit timeoutUnit = default_timeout_unit;
|
||||
|
||||
private boolean open=false;
|
||||
private boolean closed=false;
|
||||
private RuntimeException lookAheadFailure;
|
||||
|
||||
private ForwardReader<E> reader;
|
||||
private Iterator<E> iterator;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance with a result set locator.
|
||||
* @param locator the locator.
|
||||
* @throws IllegalArgumentException if the locator is <code>null</code>.
|
||||
* */
|
||||
public ResultsetStream(URI locator) throws IllegalArgumentException {
|
||||
|
||||
if (locator==null)
|
||||
throw new IllegalArgumentException("invalid or null locator");
|
||||
|
||||
this.locator=locator;
|
||||
|
||||
}
|
||||
|
||||
public void setTimeout(long timeout, TimeUnit unit) throws IllegalArgumentException {
|
||||
|
||||
if (timeout<=0 || timeoutUnit==null)
|
||||
throw new IllegalArgumentException("invalid timeout or null timeout unit");
|
||||
|
||||
this.timeout = timeout;
|
||||
this.timeoutUnit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected E delegateNext() {
|
||||
|
||||
try {
|
||||
if (lookAheadFailure!=null)
|
||||
throw lookAheadFailure;
|
||||
else
|
||||
try {
|
||||
return iterator.next();
|
||||
}
|
||||
catch(GRS2UncheckedException e) {
|
||||
|
||||
//get underlying cause
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
//rewrap checked cause as appropriate to this layer
|
||||
if (cause instanceof RuntimeException)
|
||||
throw (RuntimeException) cause;
|
||||
else
|
||||
throw new StreamException(cause);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lookAheadFailure=null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean delegateHasNext() {
|
||||
|
||||
if (closed)
|
||||
return false;
|
||||
|
||||
if (!open) {
|
||||
|
||||
try {
|
||||
reader = new ForwardReader<E>(locator);
|
||||
reader.setIteratorTimeout(timeout);
|
||||
reader.setIteratorTimeUnit(timeoutUnit);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
lookAheadFailure= new StreamOpenException("cannot open resultset "+locator,t);
|
||||
return true;
|
||||
}
|
||||
|
||||
iterator = reader.iterator();
|
||||
|
||||
log.info("initialised resultset at "+locator);
|
||||
|
||||
open=true;
|
||||
}
|
||||
|
||||
//infer outage if reader has been dismissed remotely
|
||||
if (reader.getStatus()==Status.Dispose && !closed)
|
||||
lookAheadFailure= new RuntimeException("unrecoverable failure in resultset ");
|
||||
|
||||
boolean hasNext = iterator.hasNext();
|
||||
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
if (open) {
|
||||
try {
|
||||
reader.close();
|
||||
log.info("closed resultset at "+locator);
|
||||
}
|
||||
catch(GRS2ReaderException e) {
|
||||
log.error("could not close resultset",e);
|
||||
}
|
||||
open=false;
|
||||
}
|
||||
closed=true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI locator() throws IllegalStateException {
|
||||
|
||||
if (open)
|
||||
throw new IllegalStateException("locator is invalid as result set has already been opened");
|
||||
else
|
||||
return locator;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import org.gcube.data.streams.LookAheadStream;
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* Partial implementation for {@link Stream}s that delegate to an underlying streams.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E1> the type of elements of the underlying stream
|
||||
* @param <E2> the type of elements of the stream delegate
|
||||
*/
|
||||
abstract class AbstractDelegateStream<E1,E2> extends LookAheadStream<E2> {
|
||||
|
||||
private final Stream<E1> stream;
|
||||
|
||||
/**
|
||||
* Creates an instance that delegates to a given {@link Stream}.
|
||||
* @param stream the stream
|
||||
*/
|
||||
AbstractDelegateStream(Stream<E1> stream) {
|
||||
|
||||
if (stream==null)
|
||||
throw new IllegalArgumentException("invalid null stream");
|
||||
|
||||
this.stream=stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying {@link Stream}.
|
||||
* @return the stream
|
||||
*/
|
||||
protected Stream<E1> stream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.net.URI locator() throws IllegalStateException {
|
||||
return stream.locator();
|
||||
};
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
stream.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return stream.isClosed();
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* A {@link Stream} that folds into lists the elements of another {@link Stream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream element
|
||||
*/
|
||||
public class FoldedStream<E> extends AbstractDelegateStream<E,List<E>> {
|
||||
|
||||
private final int foldSize;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and a fold size.
|
||||
* @param stream the stream
|
||||
* @param foldSize the fault size
|
||||
* @throws IllegalArgumentException if the stream is <code>null</code> or the size is not positive
|
||||
*/
|
||||
public FoldedStream(Stream<E> stream,int foldSize) throws IllegalArgumentException {
|
||||
|
||||
super(stream);
|
||||
|
||||
if (foldSize<1)
|
||||
throw new IllegalArgumentException("invalid foldsize is not positive");
|
||||
|
||||
this.foldSize=foldSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<E> delegateNext() {
|
||||
|
||||
//we do not deal with failures, streams will need to be guarded upstream
|
||||
//we also do not deal with transformations, which will need to be applied upstream
|
||||
|
||||
List<E> fold = new ArrayList<E>();
|
||||
|
||||
for (int i=0;i<foldSize;i++)
|
||||
if (stream().hasNext())
|
||||
fold.add(stream().next());
|
||||
|
||||
|
||||
return fold;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean delegateHasNext() {
|
||||
return stream().hasNext();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
|
||||
/**
|
||||
* A {@link Stream} that guards failures with a {@link FaultHandler}
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream element
|
||||
*/
|
||||
public class GuardedStream<E> extends AbstractDelegateStream<E,E> {
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and a {@link FaultHandler}
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param handler the handler
|
||||
* @throws IllegalArgumentException if the stream or the handler are <code>null</code>
|
||||
*/
|
||||
public GuardedStream(Stream<E> stream, FaultHandler handler) throws IllegalArgumentException {
|
||||
|
||||
super(stream);
|
||||
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("invalid null generator");
|
||||
|
||||
this.setHandler(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected E delegateNext() {
|
||||
return stream().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean delegateHasNext() {
|
||||
return stream().hasNext();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* A {@link Stream} that notifies an {@link StreamListener} of key iteration events.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class MonitoredStream<E> extends AbstractDelegateStream<E,E> {
|
||||
|
||||
private final StreamListener listener;
|
||||
|
||||
private boolean started=false;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and a {@link StreamListener}.
|
||||
* @param stream the stream
|
||||
* @param listener the listener
|
||||
* @throws IllegalArgumentException if the stream or the listener are {@code null}
|
||||
*/
|
||||
public MonitoredStream(Stream<E> stream,StreamListener listener) throws IllegalArgumentException {
|
||||
|
||||
super(stream);
|
||||
|
||||
if (listener==null)
|
||||
throw new IllegalArgumentException("invalid null listener");
|
||||
|
||||
this.listener=listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected E delegateNext() {
|
||||
|
||||
E element = stream().next();
|
||||
|
||||
if (!started) {
|
||||
listener.onStart();
|
||||
started=true;
|
||||
}
|
||||
|
||||
if (!delegateHasNext())
|
||||
listener.onEnd();
|
||||
|
||||
return element;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean delegateHasNext() {
|
||||
return stream().hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
listener.onClose();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* A {@link Stream} of elements generated by the elements of an input {@link Stream}
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E1> the type of elements of the input stream
|
||||
* @param <E2> the type of stream elements
|
||||
*/
|
||||
public class PipedStream<E1,E2> extends AbstractDelegateStream<E1, E2> {
|
||||
|
||||
private final Generator<E1, E2> generator;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and an element {@link Generator}.
|
||||
* @param stream the stream
|
||||
* @param generator the generator
|
||||
* @throws IllegalArgumentException if the stream or the generator are <code>null</code>
|
||||
*/
|
||||
public PipedStream(Stream<E1> stream,Generator<E1, E2> generator) throws IllegalArgumentException {
|
||||
|
||||
super(stream);
|
||||
|
||||
if (generator == null)
|
||||
throw new IllegalArgumentException("invalid null generator");
|
||||
|
||||
this.generator=generator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected E2 delegateNext() {
|
||||
return generator.yield(stream().next());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean delegateHasNext() {
|
||||
return stream().hasNext();
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* A listener of key events in the iteration of a target {@link Stream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface StreamListener {
|
||||
|
||||
/**
|
||||
* Invoked after the first element of the target {@link Stream} has been iterated over.
|
||||
*/
|
||||
void onStart();
|
||||
|
||||
/**
|
||||
* Invoked after the last element of the target {@link Stream} has been iterated over.
|
||||
*/
|
||||
void onEnd();
|
||||
|
||||
/**
|
||||
* Invoked then stream is closed.
|
||||
*/
|
||||
void onClose();
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
/**
|
||||
* Adapter implementation for {@link StreamListener}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StreamListenerAdapter implements StreamListener {
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnd() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package org.gcube.data.streams.delegates;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* A {@link Stream} of elements generated by unfolding the elements of an input {@link Stream} into multiple elements.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E1> the type of elements of the input stream
|
||||
* @param <E2> the type of stream elements
|
||||
*/
|
||||
public class UnfoldedStream<E1,E2> extends AbstractDelegateStream<E1,E2> {
|
||||
|
||||
private final Generator<E1, Stream<E2>> generator;
|
||||
private Stream<E2> unfold;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and an element {@link Generator}.
|
||||
* @param stream the stream
|
||||
* @param generator the generator
|
||||
* @throws IllegalArgumentException if the stream or the generator are <code>null</code>
|
||||
*/
|
||||
public UnfoldedStream(Stream<E1> stream,Generator<E1,Stream<E2>> generator) throws IllegalArgumentException {
|
||||
|
||||
super(stream);
|
||||
|
||||
if (generator == null)
|
||||
throw new IllegalArgumentException("invalid null generator");
|
||||
|
||||
this.generator=generator;
|
||||
}
|
||||
|
||||
private RuntimeException lookAheadFailure;
|
||||
|
||||
@Override
|
||||
protected E2 delegateNext() {
|
||||
return lookAheadFailureOrNextInUnfold();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean delegateHasNext() {
|
||||
|
||||
if (!hasUnfold())
|
||||
return false;
|
||||
|
||||
return existsInThisOrNextUnfold();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
if (unfold!=null)
|
||||
unfold.close();
|
||||
|
||||
stream().close();
|
||||
}
|
||||
|
||||
//helpers
|
||||
private boolean hasUnfold() {
|
||||
|
||||
if (unfold==null)
|
||||
if (stream().hasNext())
|
||||
try {
|
||||
unfold = generator.yield(stream().next());
|
||||
}
|
||||
catch(StreamStopSignal stop) {
|
||||
return false;
|
||||
}
|
||||
catch(StreamSkipSignal skip) {
|
||||
return hasUnfold();
|
||||
}
|
||||
catch(RuntimeException unchecked) {
|
||||
lookAheadFailure = unchecked;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean existsInThisOrNextUnfold() {
|
||||
|
||||
boolean hasNext = unfold.hasNext();
|
||||
|
||||
if (!hasNext) {
|
||||
unfold.close();
|
||||
unfold=null;
|
||||
return delegateHasNext();
|
||||
}
|
||||
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
private E2 lookAheadFailureOrNextInUnfold() {
|
||||
|
||||
try {
|
||||
if (lookAheadFailure!=null)
|
||||
throw lookAheadFailure;
|
||||
else
|
||||
return unfold.next();
|
||||
}
|
||||
finally {
|
||||
lookAheadFailure=null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package org.gcube.data.streams.dsl;
|
||||
|
||||
/**
|
||||
* A simple DSL for fault conversion.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Faults {
|
||||
|
||||
/**
|
||||
* Fault narrowing clause;
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public static class OngoingRethrowClause {
|
||||
|
||||
final RuntimeException caught;
|
||||
final Throwable cause;
|
||||
|
||||
/**
|
||||
* Creates an instance with the fault to narrow.
|
||||
*
|
||||
* @param fault the fault
|
||||
*/
|
||||
OngoingRethrowClause(RuntimeException fault) {
|
||||
this.caught=fault;
|
||||
this.cause = fault.getCause()==null?fault:fault.getCause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rethrows the fault with a narrower type, or wraps it in a {@link RuntimeException} if its type cannot be
|
||||
* narrowed.
|
||||
*
|
||||
* @param clazz1 the narrower type
|
||||
* @return unused, allows clients to throw invocations of this method
|
||||
* @throws T1 the narrower type
|
||||
*/
|
||||
public <T1 extends Throwable> RuntimeException as(Class<T1> clazz1) throws T1 {
|
||||
|
||||
if (clazz1.isInstance(cause))
|
||||
throw clazz1.cast(cause);
|
||||
|
||||
else
|
||||
|
||||
if (cause instanceof RuntimeException)
|
||||
throw (RuntimeException) cause;
|
||||
|
||||
else
|
||||
return caught;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rethrows the fault with a narrower type, or wraps it in {@link RuntimeException} if its type cannot be
|
||||
* narrowed.
|
||||
*
|
||||
* @param clazz1 the narrower type
|
||||
* @param clazz2 an alternative narrower type
|
||||
* @return unused, allows clients to throw invocations of this method
|
||||
* @throws T1 the narrower type
|
||||
* @throws T2 the second narrower type
|
||||
*/
|
||||
public <T1 extends Throwable, T2 extends Throwable> RuntimeException as(Class<T1> clazz1, Class<T2> clazz2)
|
||||
throws T1, T2 {
|
||||
|
||||
if (clazz2.isInstance(cause))
|
||||
throw clazz2.cast(cause);
|
||||
|
||||
else
|
||||
return as(clazz1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rethrows the fault with a narrower type, or wraps it in {@link RuntimeException} if its type cannot be
|
||||
* narrowed.
|
||||
*
|
||||
* @param clazz1 the narrower type
|
||||
* @param clazz2 an alternative narrower type
|
||||
* @param clazz3 an alternative narrower type
|
||||
* @return unused, allows clients to throw invocations of this method
|
||||
* @throws T1 the narrower type
|
||||
* @throws T2 the second narrower type
|
||||
* @throws T3 the second narrower type
|
||||
*/
|
||||
public <T1 extends Throwable, T2 extends Throwable, T3 extends Throwable> RuntimeException as(Class<T1> clazz1,
|
||||
Class<T2> clazz2, Class<T3> clazz3) throws T1, T2, T3 {
|
||||
|
||||
if (clazz3.isInstance(cause))
|
||||
throw clazz3.cast(cause);
|
||||
|
||||
else
|
||||
return as(clazz1, clazz2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rethrows the fault with a narrower type, or wraps it in {@link RuntimeException} if its type cannot be
|
||||
* narrowed.
|
||||
*
|
||||
* @param clazz1 the narrower type
|
||||
* @param clazz2 an alternative narrower type
|
||||
* @param clazz3 an alternative narrower type
|
||||
* @param clazz4 an alternative narrower type
|
||||
* @return unused, allows clients to throw invocations of this method
|
||||
* @throws T1 the narrower type
|
||||
* @throws T2 the second narrower type
|
||||
* @throws T3 the second narrower type
|
||||
* @throws T4 the second narrower type
|
||||
*/
|
||||
public <T1 extends Throwable, T2 extends Throwable, T3 extends Throwable, T4 extends Throwable> RuntimeException as(
|
||||
Class<T1> clazz1, Class<T2> clazz2, Class<T3> clazz3, Class<T4> clazz4) throws T1, T2, T3, T4 {
|
||||
|
||||
if (clazz4.isInstance(cause))
|
||||
throw clazz4.cast(cause);
|
||||
|
||||
else
|
||||
return as(clazz1, clazz2, clazz3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a fault to be rethrown with a narrower type.
|
||||
*
|
||||
* @param fault the fault
|
||||
* @return the next clause in the sentence
|
||||
*/
|
||||
public static OngoingRethrowClause causeOf(RuntimeException fault) {
|
||||
|
||||
return new OngoingRethrowClause(fault);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.data.streams.dsl;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Base implementation for clauses of {@link Stream} sentences.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of elements of the stream
|
||||
* @param <ENV> the type of environment in which the clause is evaluated
|
||||
*/
|
||||
public class StreamClause<E,ENV extends StreamClauseEnv<E>> {
|
||||
|
||||
protected ENV env;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given evaluation environment.
|
||||
* @param e the environment
|
||||
*/
|
||||
public StreamClause(ENV e) {
|
||||
env=e;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.data.streams.dsl;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
|
||||
/**
|
||||
* The environment in which a {@link Stream} sentence is evaluated.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of elements of the input stream
|
||||
*
|
||||
*/
|
||||
public class StreamClauseEnv<E> {
|
||||
|
||||
private final Stream<E> stream;
|
||||
|
||||
public StreamClauseEnv(Stream<E> stream) {
|
||||
this.stream=stream;
|
||||
}
|
||||
|
||||
public Stream<E> stream() {
|
||||
return stream;
|
||||
}
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
package org.gcube.data.streams.dsl;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.adapters.IteratorAdapter;
|
||||
import org.gcube.data.streams.adapters.IteratorStream;
|
||||
import org.gcube.data.streams.dsl.consume.ConsumeWithClause;
|
||||
import org.gcube.data.streams.dsl.fold.InClause;
|
||||
import org.gcube.data.streams.dsl.from.RsOfClause;
|
||||
import org.gcube.data.streams.dsl.guard.GuardWithClause;
|
||||
import org.gcube.data.streams.dsl.listen.MonitorWithClause;
|
||||
import org.gcube.data.streams.dsl.pipe.PipeThroughClause;
|
||||
import org.gcube.data.streams.dsl.publish.PublishRsUsingClause;
|
||||
import org.gcube.data.streams.dsl.publish.PublishRsWithClause;
|
||||
import org.gcube.data.streams.dsl.unfold.UnfoldThroughClause;
|
||||
import org.gcube.data.streams.generators.LoggingListener;
|
||||
import org.gcube.data.streams.generators.NoOpGenerator;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
import org.gcube.data.streams.handlers.IgnoreHandler;
|
||||
import org.gcube.data.streams.handlers.RethrowHandler;
|
||||
import org.gcube.data.streams.handlers.RethrowUnrecoverableHandler;
|
||||
import org.gcube.data.streams.handlers.StopFastHandler;
|
||||
import org.gcube.data.streams.publishers.RsStringRecordFactory;
|
||||
import org.gcube.data.streams.test.FallibleIterator;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
|
||||
/**
|
||||
*
|
||||
* The definitions of an eDSL of stream and stream-related expressions.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Streams {
|
||||
|
||||
|
||||
//CONSUME
|
||||
|
||||
/**
|
||||
* Starts a sentence to consume a {@link Stream}
|
||||
* @param stream the stream
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> ConsumeWithClause<E> consume(Stream<E> stream) {
|
||||
return new ConsumeWithClause<E>(stream);
|
||||
}
|
||||
|
||||
//CONVERT
|
||||
|
||||
/**
|
||||
* Converts an {@link Iterator} to a {@link Stream}.
|
||||
* @param itarator the iterator
|
||||
* @return the stream
|
||||
*/
|
||||
public static <E> Stream<E> convert(Iterator<E> itarator) {
|
||||
return new IteratorStream<E>(itarator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a custom {@link IteratorAdapter} to a {@link Stream}.
|
||||
* @param adapter the adapter
|
||||
* @return the stream
|
||||
*/
|
||||
public static <E> IteratorStream<E> convert(IteratorAdapter<E> adapter) {
|
||||
return new IteratorStream<E>(adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link Iterable} to a {@link Stream}.
|
||||
* @param iterable the iterable
|
||||
* @return the stream
|
||||
*/
|
||||
public static <E> Stream<E> convert(Iterable<E> iterable) {
|
||||
return convert(iterable.iterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one or more elements into a {@link Stream}.
|
||||
* @param elements the elements
|
||||
* @return the stream
|
||||
*/
|
||||
public static <E> Stream<E> convert(E ...elements) {
|
||||
return convert(asList(elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a mixture of exceptions and elements of a given type to a {@link Stream} of a that type.
|
||||
* It's the client's responsibility to ensure that the elements that are not exceptions are homogeneously typed as the type indicated in input.
|
||||
* @param clazz the stream type
|
||||
* @param elements the elements
|
||||
* @return the stream
|
||||
*/
|
||||
public static <E> Stream<E> convertWithFaults(Class<E> clazz, Object...elements) {
|
||||
return convertWithFaults(clazz,asList(elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a mixture of exceptions and elements of a given type to a {@link Stream} of a that type.
|
||||
* It's the client's responsibility to ensure that the elements that are not exceptions are homogeneously typed as the type indicated in input.
|
||||
* @param clazz the stream type
|
||||
* @param elements the elements
|
||||
* @return the stream
|
||||
*/
|
||||
public static <E> Stream<E> convertWithFaults(Class<E> clazz,List<? extends Object> elements) {
|
||||
|
||||
return convert(new FallibleIterator<E>(clazz, elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a sentence to convert a resultset into a {@link Stream}.
|
||||
* @param locator the locator of the resultset
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static RsOfClause<Record> convert(URI locator) {
|
||||
return new RsOfClause<Record>(locator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} of strings extracted from a resultset of {@link RsStringRecordFactory#STRING_RECORD}s.
|
||||
* @param locator the locator of the resultset
|
||||
* @return the stream
|
||||
*/
|
||||
public static Stream<String> stringsIn(URI locator) {
|
||||
return convert(locator).ofStrings().withDefaults();
|
||||
}
|
||||
|
||||
|
||||
// PIPE
|
||||
|
||||
/**
|
||||
* Starts a sentence to produce a {@link Stream} generated from another {@link Stream}.
|
||||
* @param stream the input stream.
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> PipeThroughClause<E> pipe(Stream<E> stream) {
|
||||
return new PipeThroughClause<E>(stream);
|
||||
}
|
||||
|
||||
|
||||
// FOLD
|
||||
|
||||
/**
|
||||
* Starts a sentence to produce a {@link Stream} that groups of elements of another {@link Stream}.
|
||||
* @param stream the input stream.
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> InClause<E> fold(Stream<E> stream) {
|
||||
return new InClause<E>(stream);
|
||||
}
|
||||
|
||||
// UNFOLD
|
||||
|
||||
/**
|
||||
* Starts a sentence to produce a {@link Stream} that unfolds the elements of another {@link Stream}.
|
||||
* @param stream the input stream.
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> UnfoldThroughClause<E> unfold(Stream<E> stream) {
|
||||
return new UnfoldThroughClause<E>(stream);
|
||||
}
|
||||
|
||||
// GUARD
|
||||
|
||||
/**
|
||||
* Starts a sentence to produce a {@link Stream} that controls the error raised by another {@link Stream}.
|
||||
* @param stream the input stream.
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> GuardWithClause<E> guard(Stream<E> stream) {
|
||||
return new GuardWithClause<E>(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a sentence to produce a {@link Stream} that notifies key events in the iteration of another {@link Stream}.
|
||||
* @param stream the input stream.
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> MonitorWithClause<E> monitor(Stream<E> stream) {
|
||||
return new MonitorWithClause<E>(stream);
|
||||
}
|
||||
|
||||
// PUBLISH
|
||||
|
||||
/**
|
||||
* Starts a sentence to publish a {@link Stream} as a resultset.
|
||||
* @param stream the stream
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static <E> PublishRsUsingClause<E> publish(Stream<E> stream) {
|
||||
return new PublishRsUsingClause<E>(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a sentence to publish a {@link Stream} as a resultset.
|
||||
* @param stream the stream
|
||||
* @return the next clause of the sentence
|
||||
*/
|
||||
public static PublishRsWithClause<String> publishStringsIn(Stream<String> stream) {
|
||||
return new PublishRsUsingClause<String>(stream).using(no_serialiser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} that logs the throughput of an input {@link Stream}.
|
||||
* @param stream the input stream
|
||||
* @return the output stream
|
||||
*/
|
||||
public static <E> Stream<E> log(Stream<E> stream) {
|
||||
LoggingListener<E> listener = new LoggingListener<E>();
|
||||
return monitor(pipe(stream).through(listener)).with(listener);
|
||||
}
|
||||
|
||||
// GENERATORS
|
||||
|
||||
/**
|
||||
* A {@link NoOpGenerator}.
|
||||
*/
|
||||
public static NoOpGenerator<String> no_serialiser = new NoOpGenerator<String>();
|
||||
|
||||
/**
|
||||
* Returns a {@link NoOpGenerator}.
|
||||
* @return the generator
|
||||
*/
|
||||
public static <E> NoOpGenerator<E> no_op(Stream<E> stream) {
|
||||
return new NoOpGenerator<E>();
|
||||
}
|
||||
|
||||
// HANDLERS
|
||||
/**
|
||||
* A {@link RethrowHandler} for failure handling.
|
||||
*/
|
||||
public static FaultHandler RETHROW_POLICY = new RethrowHandler();
|
||||
|
||||
/**
|
||||
* A {@link RethrowUnrecoverableHandler} for failure handling.
|
||||
*/
|
||||
public static FaultHandler RETHROW_UNRECOVERABLE_POLICY = new RethrowUnrecoverableHandler();
|
||||
|
||||
/**
|
||||
* A {@link StopFastHandler} for failure handling.
|
||||
*/
|
||||
public static FaultHandler STOPFAST_POLICY= new StopFastHandler();
|
||||
|
||||
/**
|
||||
* A {@link IgnoreHandler} for failure handling.
|
||||
*/
|
||||
public static FaultHandler IGNORE_POLICY = new IgnoreHandler();
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.gcube.data.streams.dsl.consume;
|
||||
|
||||
import org.gcube.data.streams.Callback;
|
||||
import org.gcube.data.streams.StreamConsumer;
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
|
||||
/**
|
||||
* The clause of {@code consume} sentences in which a {@link Callback} is configured on the input stream.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class ConsumeWithClause<E> extends StreamClause<E, StreamClauseEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance from an input {@link Stream}
|
||||
*
|
||||
* @param stream the stream
|
||||
*/
|
||||
public ConsumeWithClause(Stream<E> stream) {
|
||||
super(new StreamClauseEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Stream} configured with a given {@link Callback}.
|
||||
*
|
||||
* @param consumer the consumer
|
||||
*/
|
||||
public <E2> void with(Callback<E> consumer) {
|
||||
new StreamConsumer<E>(env.stream(), consumer).start();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.gcube.data.streams.dsl.fold;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.delegates.FoldedStream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
|
||||
/**
|
||||
* The clause of {@code fold} sentences in which a fold size is configured for
|
||||
* the output stream.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class InClause<E> extends StreamClause<E,StreamClauseEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance with an input {@link Stream}.
|
||||
* @param stream the stream
|
||||
*/
|
||||
public InClause(Stream<E> stream) {
|
||||
super(new StreamClauseEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} that folds the element of the input {@link Stream} in lists of a given size.
|
||||
* @param foldSize the size
|
||||
* @return the stream
|
||||
*/
|
||||
public FoldedStream<E> in(int foldSize) {
|
||||
return new FoldedStream<E>(env.stream(),foldSize);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.gcube.data.streams.dsl.from;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
|
||||
/**
|
||||
* Partial clause implementation for {@code convert} sentences.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
abstract class RsClause<R extends Record> {
|
||||
|
||||
protected RsEnv<R> env;
|
||||
|
||||
/**
|
||||
* Creates an instance from a {@link RsEnv}
|
||||
* @param env the environment
|
||||
*/
|
||||
public RsClause(RsEnv<R> env) {
|
||||
|
||||
this.env=env;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.data.streams.dsl.from;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
|
||||
/**
|
||||
* The environment in which {@code convert} sentences are evaluated.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
class RsEnv<R extends Record> {
|
||||
|
||||
protected URI locator;
|
||||
|
||||
protected Class<R> recordClass;
|
||||
|
||||
/**
|
||||
* Creates a new instance from a resultset locator
|
||||
* @param locator the locator
|
||||
*/
|
||||
public RsEnv(URI locator) {
|
||||
this.locator=locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from a resultset locator
|
||||
* @param locator the locator
|
||||
* @param recordClass the class;
|
||||
*/
|
||||
public RsEnv(URI locator, Class<R> recordClass) {
|
||||
this.locator=locator;
|
||||
this.recordClass = recordClass;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package org.gcube.data.streams.dsl.from;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.GenericRecord;
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.gcube.data.streams.publishers.RsStringRecordFactory;
|
||||
|
||||
/**
|
||||
* A {@link RsClause} in which the record type of the input resultset is configured.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RsOfClause<R extends Record> extends RsClause<R> {
|
||||
|
||||
/**
|
||||
* Creates an instance from a resultset.
|
||||
* @param locator the locator of the resultset
|
||||
*/
|
||||
public RsOfClause(URI locator) {
|
||||
super(new RsEnv<R>(locator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the type of records in the input resultset
|
||||
* @param clazz the record type
|
||||
* @return the next clause in the sentence
|
||||
*/
|
||||
public <R2 extends R> RsWithClause<R2> of(Class<R2> clazz) {
|
||||
return new RsWithClause<R2>(new RsEnv<R2>(env.locator,clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the type of records in the input result set to {@link RsStringRecordFactory#STRING_RECORD}.
|
||||
* @return the next clause in the sentence
|
||||
*/
|
||||
public RsStringWithClause ofStrings() {
|
||||
return new RsStringWithClause(new RsEnv<GenericRecord>(env.locator));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.gcube.data.streams.dsl.from;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.GenericRecord;
|
||||
import gr.uoa.di.madgik.grs.record.field.StringField;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.adapters.ResultsetStream;
|
||||
import org.gcube.data.streams.dsl.Streams;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* A {@link RsClause} in which the adapter of the input resultset is configured.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RsStringWithClause extends RsClause<GenericRecord> {
|
||||
|
||||
// used internally to extract strings from records
|
||||
private static Generator<GenericRecord, String> recordSerialiser = new Generator<GenericRecord, String>() {
|
||||
@Override
|
||||
public String yield(GenericRecord element) {
|
||||
return ((StringField) element.getField(0)).getPayload();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link RsEnv}.
|
||||
*
|
||||
* @param env the environment
|
||||
*/
|
||||
public RsStringWithClause(RsEnv<GenericRecord> env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} with a given read timeout on the input resultset
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param unit the time unit of the timeout
|
||||
* @return the stream
|
||||
*/
|
||||
public Stream<String> withTimeout(int timeout, TimeUnit unit) {
|
||||
ResultsetStream<GenericRecord> recordStream = new ResultsetStream<GenericRecord>(env.locator);
|
||||
recordStream.setTimeout(timeout, unit);
|
||||
return Streams.pipe(recordStream).through(recordSerialiser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} with a {@link ResultsetStream#default_timeout} in a
|
||||
* {@link ResultsetStream#default_timeout_unit} on the input resultset.
|
||||
*
|
||||
* @return the stream
|
||||
*/
|
||||
public Stream<String> withDefaults() {
|
||||
ResultsetStream<GenericRecord> recordStream = new ResultsetStream<GenericRecord>(env.locator);
|
||||
return Streams.pipe(recordStream).through(recordSerialiser);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package org.gcube.data.streams.dsl.from;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.adapters.ResultsetStream;
|
||||
import org.gcube.data.streams.dsl.Streams;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* A {@link RsClause} in which the adapter of the input resultset is configured.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <R> the {@link Record} type of stream elements
|
||||
*/
|
||||
public class RsWithClause<R extends Record> extends RsClause<R> {
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link RsEnv}.
|
||||
*
|
||||
* @param env the environment
|
||||
*/
|
||||
public RsWithClause(RsEnv<R> env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} with a given read timeout on the input resultset.
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param unit the time unit of the timeout
|
||||
* @return the stream
|
||||
*/
|
||||
public Stream<R> withTimeout(int timeout, TimeUnit unit) {
|
||||
ResultsetStream<Record> stream = new ResultsetStream<Record>(env.locator);
|
||||
stream.setTimeout(timeout, unit);
|
||||
return Streams.pipe(stream).through(new RecordDeserialiser<R>(env.recordClass));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} with a {@link ResultsetStream#default_timeout} in a
|
||||
* {@link ResultsetStream#default_timeout_unit} on the input resultset.
|
||||
*
|
||||
* @return the stream
|
||||
*/
|
||||
public Stream<R> withDefaults() {
|
||||
return new ResultsetStream<R>(env.locator);
|
||||
}
|
||||
|
||||
// used internally to extract strings from records
|
||||
private static class RecordDeserialiser<R extends Record> implements Generator<Record,R> {
|
||||
|
||||
private final Class<R> recordClass;
|
||||
|
||||
public RecordDeserialiser(Class<R> recordClass) {
|
||||
this.recordClass=recordClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R yield(Record element) {
|
||||
return recordClass.cast(element);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.gcube.data.streams.dsl.guard;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.delegates.GuardedStream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
|
||||
/**
|
||||
* The clause of {@code fold} sentences of the {@link Stream} DSL in which a {@link FaultHandler} is configured on
|
||||
* the output stream.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class GuardWithClause<E> extends StreamClause<E,StreamClauseEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance with an input {@link Stream}.
|
||||
* @param stream the stream
|
||||
*/
|
||||
public GuardWithClause(Stream<E> stream) {
|
||||
super(new StreamClauseEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Stream} with configured with a given {@link FaultHandler}.
|
||||
* @param handler the handler
|
||||
* @return the stream
|
||||
*/
|
||||
public GuardedStream<E> with(FaultHandler handler) {
|
||||
return new GuardedStream<E>(env.stream(),handler);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.gcube.data.streams.dsl.listen;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.delegates.MonitoredStream;
|
||||
import org.gcube.data.streams.delegates.StreamListener;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
|
||||
/**
|
||||
* The clause of {@code guard} sentences in which a {@link StreamListener} is configured on the stream.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class MonitorWithClause<E> extends StreamClause<E, StreamClauseEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance from an input {@link Stream}
|
||||
*
|
||||
* @param stream the stream
|
||||
*/
|
||||
public MonitorWithClause(Stream<E> stream) {
|
||||
super(new StreamClauseEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Stream} configured with a given {@link StreamListener}.
|
||||
*
|
||||
* @param listener the listener
|
||||
* @return the stream
|
||||
*/
|
||||
public MonitoredStream<E> with(StreamListener listener) {
|
||||
return new MonitoredStream<E>(env.stream(), listener);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.gcube.data.streams.dsl.pipe;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.delegates.PipedStream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* The clause of {@code pipe} sentences in which a {@link Generator} is configured on the output stream.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class PipeThroughClause<E> extends StreamClause<E, StreamClauseEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance from an input {@link Stream}
|
||||
*
|
||||
* @param stream the stream
|
||||
*/
|
||||
public PipeThroughClause(Stream<E> stream) {
|
||||
super(new StreamClauseEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Stream} configured with a given {@link Generator}.
|
||||
*
|
||||
* @param generator the generator
|
||||
* @return the stream
|
||||
*/
|
||||
public <E2> PipedStream<E, E2> through(Generator<E, E2> generator) {
|
||||
return new PipedStream<E, E2>(env.stream(), generator);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.data.streams.dsl.publish;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
import org.gcube.data.streams.publishers.RecordFactory;
|
||||
|
||||
|
||||
/**
|
||||
* The {@link StreamClauseEnv} in which {@code publish} sentences are evaluated.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of elements of the input stream
|
||||
*
|
||||
*/
|
||||
public class PublishRsEnv<E> extends StreamClauseEnv<E> {
|
||||
|
||||
RecordFactory<E> factory;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream}
|
||||
* @param stream the stream
|
||||
*/
|
||||
public PublishRsEnv(Stream<E> stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Stream} and a {@link RecordFactory}
|
||||
* @param stream the stream
|
||||
* @param factory the factory
|
||||
*/
|
||||
public PublishRsEnv(Stream<E> stream, RecordFactory<E> factory) {
|
||||
super(stream);
|
||||
this.factory=factory;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.data.streams.dsl.publish;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.publishers.RecordFactory;
|
||||
import org.gcube.data.streams.publishers.RsStringRecordFactory;
|
||||
|
||||
/**
|
||||
* The clause of {@code publish} sentences in which the type of {@link Record}s is configured on the output resultset.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class PublishRsUsingClause<E> extends StreamClause<E,PublishRsEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance with an input {@link Stream}.
|
||||
* @param stream the stream
|
||||
*/
|
||||
public PublishRsUsingClause(Stream<E> stream) {
|
||||
super(new PublishRsEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a serialiser for the elements of the input {@link Stream}.
|
||||
* @param serialiser the serialiser
|
||||
* @return the next clause in the sentence
|
||||
*/
|
||||
public PublishRsWithClause<E> using(Generator<E,String> serialiser) {
|
||||
env.factory = new RsStringRecordFactory<E>(serialiser);
|
||||
return new PublishRsWithClause<E>(env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a {@link RecordFactory} for the elements of the input {@link Stream}.
|
||||
* @param factory the factory
|
||||
* @return the next clause in the sentence
|
||||
*/
|
||||
public PublishRsWithClause<E> using(RecordFactory<E> factory) {
|
||||
env.factory = factory;
|
||||
return new PublishRsWithClause<E>(env);
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.data.streams.dsl.publish;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
import org.gcube.data.streams.publishers.ThreadProvider;
|
||||
import org.gcube.data.streams.publishers.RsPublisher;
|
||||
|
||||
/**
|
||||
* The clause of {@code publish} sentences in which the output resultset is configured.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class PublishRsWithClause<E> extends StreamClause<E, PublishRsEnv<E>> {
|
||||
|
||||
private final RsPublisher<E> publisher;
|
||||
|
||||
/**
|
||||
* Creates an instance with the {@link PublishRsEnv} of the ongoing sentence.
|
||||
*
|
||||
* @param e the environment
|
||||
*/
|
||||
public PublishRsWithClause(PublishRsEnv<E> e) {
|
||||
super(e);
|
||||
this.publisher = new RsPublisher<E>(e.stream(), e.factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the element capacity of the resultset buffer.
|
||||
*
|
||||
* @param size the number of elements in the buffer
|
||||
* @return this clause
|
||||
*/
|
||||
public PublishRsWithClause<E> withBufferOf(int size) {
|
||||
publisher.setBufferSize(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the publishing timeout of the resultset.
|
||||
* <p>
|
||||
* If the timeout expire, elements of the input {@link Stream} will no longer be published, though they may still be
|
||||
* consumed if {@link #nonstop()} has been invoked.
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param unit the time unit for the timeout
|
||||
* @return this clause
|
||||
*/
|
||||
public PublishRsWithClause<E> withTimeoutOf(int timeout, TimeUnit unit) {
|
||||
publisher.setTimeout(timeout, unit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures publication to continue consuming the input {@link Stream} after the expiry of the publishing timeout.
|
||||
* <p>
|
||||
* Typically used for the side-effects of publications.
|
||||
*
|
||||
* @return this clause
|
||||
*/
|
||||
public PublishRsWithClause<E> nonstop() {
|
||||
publisher.setOnDemand(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a {@link ThreadProvider} for the publishing thread.
|
||||
* <p>
|
||||
* Publication occurs asynchronously and a thread provider may be required to make available thread-bound
|
||||
* information in the publishing thread.
|
||||
*
|
||||
* @param provider the thread provider
|
||||
* @return this clause
|
||||
*/
|
||||
public PublishRsWithClause<E> with(ThreadProvider provider) {
|
||||
publisher.setThreadProvider(provider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a {@link FaultHandler} for publication and returns the locator of the resultset.
|
||||
*
|
||||
* @param handler the handler
|
||||
* @return the locator the locator
|
||||
*/
|
||||
public URI with(FaultHandler handler) {
|
||||
publisher.setFaultHandler(handler);
|
||||
return publisher.publish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locator of the resultset.
|
||||
*
|
||||
* @return the locator.
|
||||
*/
|
||||
public URI withDefaults() {
|
||||
return publisher.publish();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.gcube.data.streams.dsl.unfold;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.delegates.UnfoldedStream;
|
||||
import org.gcube.data.streams.dsl.StreamClause;
|
||||
import org.gcube.data.streams.dsl.StreamClauseEnv;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* The clause of {@code unfold} sentences in which a {@link Generator} is configured on the output stream.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class UnfoldThroughClause<E> extends StreamClause<E, StreamClauseEnv<E>> {
|
||||
|
||||
/**
|
||||
* Creates an instance from an input {@link Stream}
|
||||
*
|
||||
* @param stream the stream
|
||||
*/
|
||||
public UnfoldThroughClause(Stream<E> stream) {
|
||||
super(new StreamClauseEnv<E>(stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Stream} configured with a given {@link Generator}.
|
||||
*
|
||||
* @param generator the generator
|
||||
* @return the stream
|
||||
*/
|
||||
public <E2> UnfoldedStream<E, E2> through(Generator<E, Stream<E2>> generator) {
|
||||
return new UnfoldedStream<E, E2>(env.stream(), generator);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.gcube.data.streams.exceptions;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* Intended for {@link Exception} classes to mark them as unrecoverable for iteration purposes.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface StreamContingency {}
|
@ -0,0 +1,43 @@
|
||||
package org.gcube.data.streams.exceptions;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* A failure that occurs when first accessing a {@link Stream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StreamException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
public StreamException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given cause.
|
||||
* @param cause the cause
|
||||
*/
|
||||
public StreamException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given message and a given cause.
|
||||
* @param msg the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public StreamException(String msg, Throwable cause) {
|
||||
super(msg,cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.gcube.data.streams.exceptions;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* A failure that occurs when a {@link Stream} cannot be opened.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StreamOpenException extends StreamException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given cause.
|
||||
* @param cause the cause
|
||||
*/
|
||||
public StreamOpenException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given message and a given cause.
|
||||
* @param msg the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public StreamOpenException(String msg, Throwable cause) {
|
||||
super(msg,cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.gcube.data.streams.exceptions;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* A failure that occurs when a {@link Stream} cannot be published.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StreamPublishException extends StreamException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given cause.
|
||||
* @param cause the cause
|
||||
*/
|
||||
public StreamPublishException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given message and a given cause.
|
||||
* @param msg the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public StreamPublishException(String msg, Throwable cause) {
|
||||
super(msg,cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.gcube.data.streams.exceptions;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
|
||||
/**
|
||||
* Used in {@link Generator}s or {@link FaultHandler}s to signals that the current element of a {@link Stream} should be skipped.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StreamSkipSignal extends StreamException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
public StreamSkipSignal() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package org.gcube.data.streams.exceptions;
|
||||
|
||||
import org.gcube.data.streams.Callback;
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
|
||||
/**
|
||||
* Used internally by {@link FaultHandler}s and {@link Callback}s to require the premature end of an iteration over a
|
||||
* {@link Stream}
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StreamStopSignal extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.gcube.data.streams.generators;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
|
||||
/**
|
||||
* A partial implementation of {@link Filter} that provides support for skipping elements
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E1> the type of input elements
|
||||
* @param <E2> the type of yielded elements
|
||||
*/
|
||||
public abstract class Filter<E1,E2> implements Generator<E1,E2> {
|
||||
|
||||
private final StreamSkipSignal skip = new StreamSkipSignal();
|
||||
|
||||
/**
|
||||
* Invoked to skip the current element.
|
||||
*/
|
||||
protected void skip() {
|
||||
throw skip;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.gcube.data.streams.generators;
|
||||
|
||||
import org.gcube.data.streams.Iteration;
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
|
||||
/**
|
||||
* Yields elements of a {@link Stream} from elements of another {@link Stream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E1> the type of elements in the input stream
|
||||
* @param <E2> the type of elements in the output stream
|
||||
*
|
||||
* @see Stream
|
||||
*/
|
||||
public interface Generator<E1, E2> {
|
||||
|
||||
/** The ongoing iteration. */
|
||||
static final Iteration iteration = new Iteration();
|
||||
|
||||
|
||||
/**
|
||||
* Yields an element of a {@link Stream} from an element of another {@link Stream}.
|
||||
*
|
||||
* @param element the input element
|
||||
* @return the output element
|
||||
* @throws StreamSkipSignal if no element <em>should</em> be yielded from the input element (i.e. the element should
|
||||
* not contribute to the output stream)
|
||||
* @throws StreamStopSignal if no further element should be yielded
|
||||
* @throws RuntimeException if no element <em>can</em> be yielded from the input element
|
||||
*/
|
||||
E2 yield(E1 element) throws StreamSkipSignal,StreamStopSignal;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package org.gcube.data.streams.generators;
|
||||
|
||||
import static java.lang.Math.*;
|
||||
import static java.lang.System.*;
|
||||
|
||||
import org.gcube.data.streams.delegates.StreamListener;
|
||||
import org.gcube.data.streams.delegates.StreamListenerAdapter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A pass-through {@link Generator} that acts as a {@link StreamListener} for throughput logging purposes.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class LoggingListener<E> extends StreamListenerAdapter implements Generator<E,E> {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(LoggingListener.class);
|
||||
|
||||
static long count=0;
|
||||
static long starttime=0;
|
||||
|
||||
static private String rformat = "streamed %1d elements in %2d ms (%3d/sec)";
|
||||
|
||||
|
||||
/**{@inheritDoc}*/
|
||||
@Override
|
||||
public void onStart() {
|
||||
log.info("started processing");
|
||||
starttime=currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
/**{@inheritDoc}*/
|
||||
@Override
|
||||
public E yield(E element) {
|
||||
|
||||
count++;
|
||||
return element;
|
||||
}
|
||||
|
||||
/**{@inheritDoc}*/
|
||||
@Override
|
||||
public void onClose() {
|
||||
|
||||
String report;
|
||||
if (starttime==0) //we may have iterated over no elements at all
|
||||
report="processed 0 elements";
|
||||
else {
|
||||
long time= currentTimeMillis()-starttime;
|
||||
long ratio = round(((double)count/time)*1000);
|
||||
report = String.format(rformat,count,time,ratio);
|
||||
}
|
||||
|
||||
log.info(report);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.gcube.data.streams.generators;
|
||||
|
||||
|
||||
/**
|
||||
* A pass-through {@link Generator}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class NoOpGenerator<E> implements Generator<E,E> {
|
||||
|
||||
@Override
|
||||
public E yield(E element) {
|
||||
return element;
|
||||
};
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package org.gcube.data.streams.generators;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
|
||||
/**
|
||||
* A {@link Filter} that changes the elements of the input {@link Stream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of input elements
|
||||
*/
|
||||
public abstract class Processor<E> extends Filter<E, E> {
|
||||
|
||||
@Override
|
||||
public final E yield(E element) {
|
||||
|
||||
process(element);
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes an element of a {@link Stream}.
|
||||
*
|
||||
* @param element the input element
|
||||
* @throws StreamSkipSignal if no element <em>should</em> be yielded from the input element (i.e. the element
|
||||
* should not contribute to the output stream)
|
||||
* @throws RuntimeException ion if no element <em>can</em> be yielded from the input element
|
||||
*/
|
||||
protected abstract void process(E element);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A partial implementation for {@link FaultHandler} that count failures and keep track of the last observed failure.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public abstract class CountingHandler implements FaultHandler {
|
||||
|
||||
private int count;
|
||||
private Exception lastFailure;
|
||||
|
||||
@Override
|
||||
public final void handle(RuntimeException failure) {
|
||||
handle(failure, lastFailure, count);
|
||||
count++;
|
||||
lastFailure = failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether iteration should continue or stop the iteration on the occurrence of an iteration failure.
|
||||
*
|
||||
* @param failure the failure
|
||||
* @param lastFailure the failure observed previously, or <code>null</code> if this is the first observed failure
|
||||
* @param failureCount the number of failures counted so far, <code>0</code> if this is the first observed failure
|
||||
* @throws RuntimeException if no element can be yielded from the input element
|
||||
*/
|
||||
protected abstract void handle(Exception failure, Exception lastFailure, int failureCount);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
import org.gcube.data.streams.Iteration;
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* Handlers of {@link Stream} iteration failures.
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface FaultHandler {
|
||||
|
||||
/** The ongoing iteration. */
|
||||
static final Iteration iteration = new Iteration();
|
||||
|
||||
/**
|
||||
* Indicates whether iteration should continue or stop the iteration on the occurrence of an iteration failure.
|
||||
* @param failure the failure
|
||||
* @throws RuntimeException if no element can be yielded from the input element
|
||||
*
|
||||
*/
|
||||
void handle(RuntimeException failure);
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link FaultHandler} that silently absorbs all failures.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class IgnoreHandler implements FaultHandler {
|
||||
|
||||
@Override
|
||||
public void handle(RuntimeException failure) {}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link FaultHandler} that rethrows all failures (i.e. does not handle any).
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RethrowHandler implements FaultHandler {
|
||||
|
||||
@Override
|
||||
public void handle(RuntimeException failure) {
|
||||
|
||||
throw failure;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
import static org.gcube.data.streams.Utils.*;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamContingency;
|
||||
|
||||
/**
|
||||
* A {@link FaultHandler} that silently absorbs {@link StreamContingency}s
|
||||
* but re-throws all other failures.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RethrowUnrecoverableHandler implements FaultHandler {
|
||||
|
||||
@Override
|
||||
public void handle(RuntimeException failure) {
|
||||
|
||||
if (!isContingency(failure))
|
||||
throw failure;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link FaultHandler} that silently stops iteration at the first occurrence of any failure.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StopFastHandler implements FaultHandler {
|
||||
|
||||
@Override
|
||||
public void handle(RuntimeException failure) {
|
||||
|
||||
iteration.stop();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.gcube.data.streams.handlers;
|
||||
|
||||
import static org.gcube.data.streams.Utils.*;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamContingency;
|
||||
|
||||
/**
|
||||
* A {@link FaultHandler} that silently absorbs {@link StreamContingency}s
|
||||
* and stops iteration at the first unrecoverable failure.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class StopUnrecoverableHandler implements FaultHandler {
|
||||
|
||||
@Override
|
||||
public void handle(RuntimeException failure) {
|
||||
|
||||
if (!isContingency(failure))
|
||||
iteration.stop();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.gcube.data.streams.publishers;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
import gr.uoa.di.madgik.grs.record.RecordDefinition;
|
||||
|
||||
import org.gcube.data.streams.Iteration;
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* Generates {@link Record}s from the elements of a {@link Stream}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of the elements
|
||||
*/
|
||||
public interface RecordFactory<E> {
|
||||
|
||||
/** The ongoing iteration. */
|
||||
static final Iteration iteration = new Iteration();
|
||||
|
||||
/**
|
||||
* Returns the definitions of the records.
|
||||
* @return the definitions
|
||||
*/
|
||||
RecordDefinition[] definitions();
|
||||
|
||||
/**
|
||||
* Generates a {@link Record} from a {@link Stream} element.
|
||||
* @param element the element
|
||||
* @return the record
|
||||
* @throws RuntimeException if no record can be generated from the input element
|
||||
*/
|
||||
Record newRecord(E element);
|
||||
}
|
@ -0,0 +1,312 @@
|
||||
package org.gcube.data.streams.publishers;
|
||||
|
||||
import static gr.uoa.di.madgik.grs.writer.RecordWriter.*;
|
||||
import static org.gcube.data.streams.Utils.*;
|
||||
import gr.uoa.di.madgik.grs.buffer.IBuffer.Status;
|
||||
import gr.uoa.di.madgik.grs.record.Record;
|
||||
import gr.uoa.di.madgik.grs.writer.GRS2WriterException;
|
||||
import gr.uoa.di.madgik.grs.writer.RecordWriter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.Utils;
|
||||
import org.gcube.data.streams.exceptions.StreamPublishException;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
import org.gcube.data.streams.handlers.RethrowHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Publishes {@link Stream}s as resultsets.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream element
|
||||
*
|
||||
*/
|
||||
public class RsPublisher<E> implements StreamPublisher {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(RsPublisher.class);
|
||||
|
||||
private final Stream<E> stream;
|
||||
private final RecordFactory<E> factory;
|
||||
|
||||
private RsTransport transport;
|
||||
|
||||
// resultset writer parameters
|
||||
private int bufferSize = DefaultBufferCapacity;
|
||||
private long timeout = DefaultInactivityTimeout;
|
||||
private TimeUnit timeoutUnit = DefaultInactivityTimeUnit;
|
||||
private boolean onDemand = true;
|
||||
|
||||
// default thread provider
|
||||
private ThreadProvider provider = new ThreadProvider() {
|
||||
@Override
|
||||
public Thread newThread(Runnable task) {
|
||||
return new Thread(task);
|
||||
}
|
||||
};
|
||||
|
||||
// by default, we re-throw failures and handle them in publishing loop
|
||||
private FaultHandler handler = new RethrowHandler();
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link Stream} and with a given element serialiser.
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param serialiser the serialiser
|
||||
*/
|
||||
public RsPublisher(Stream<E> stream, Generator<E, String> serialiser) {
|
||||
this(stream, new RsStringRecordFactory<E>(serialiser));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link Stream} and with a given {@link RecordFactory}.
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param factory the factory
|
||||
*/
|
||||
public RsPublisher(Stream<E> stream, RecordFactory<E> factory) {
|
||||
|
||||
if (stream == null || factory == null || factory.definitions() == null)
|
||||
throw new IllegalArgumentException("invalid or null inputs");
|
||||
|
||||
this.stream = stream;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the write buffer.
|
||||
*
|
||||
* @param size the size in bytes.
|
||||
* @throws IllegalArgumentException if the size is not a positive number
|
||||
*/
|
||||
public void setBufferSize(int size) throws IllegalArgumentException {
|
||||
|
||||
if (size <= 0)
|
||||
throw new IllegalArgumentException("invalid empty buffer");
|
||||
|
||||
this.bufferSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time to wait on a full write buffer. After the time has elapsed publication stops.
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param timeoutUnit the timeout unit
|
||||
* @throws IllegalArgumentException if the timeout is not a positive number or the timeout unit is <code>null</code>
|
||||
*/
|
||||
public void setTimeout(long timeout, TimeUnit timeoutUnit) throws IllegalArgumentException {
|
||||
|
||||
if (timeout <= 0 || timeoutUnit == null)
|
||||
throw new IllegalArgumentException("invalid timeout or time unit");
|
||||
|
||||
this.timeout = timeout;
|
||||
this.timeoutUnit = timeoutUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resultset transport.
|
||||
*
|
||||
* @param transport the transport
|
||||
* @throws IllegalArgumentException if the transport is <code>null</code>
|
||||
*/
|
||||
public void setTransport(RsTransport transport) {
|
||||
|
||||
if (transport == null)
|
||||
throw new IllegalArgumentException("invalid null transport");
|
||||
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the production mode of the publiher
|
||||
*
|
||||
* @param onDemand <code>true</code> if the stream ought to be consumed only when clients require it,
|
||||
* <code>false</code> if it should be consumed continuously.
|
||||
*/
|
||||
public void setOnDemand(boolean onDemand) {
|
||||
this.onDemand = onDemand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ThreadProvider} used to populate the resultset.
|
||||
*
|
||||
* @param provider the provider
|
||||
* @throws IllegalArgumentException if the provider is <code>null</code>.
|
||||
*/
|
||||
public void setThreadProvider(ThreadProvider provider) {
|
||||
|
||||
if (provider == null)
|
||||
throw new IllegalArgumentException("invalid null provider");
|
||||
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link FaultHandler} for reading and writing failures.
|
||||
*
|
||||
* @param handler the handler
|
||||
* @throws IllegalArgumentException if the handler is <code>null</code>
|
||||
*/
|
||||
public void setFaultHandler(FaultHandler handler) {
|
||||
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("invalid null handler");
|
||||
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI publish() throws StreamPublishException {
|
||||
|
||||
Utils.initialiseRS();
|
||||
|
||||
if (transport == null)
|
||||
transport = RsTransport.TCP;
|
||||
|
||||
URI locator;
|
||||
RecordWriter<Record> writer;
|
||||
|
||||
// publish
|
||||
try {
|
||||
|
||||
writer = new RecordWriter<Record>(transport.proxy(), // The proxy that defines the way the writer can be
|
||||
// accessed
|
||||
factory.definitions(), // The definitions of the records the gRS handles
|
||||
bufferSize, // The capacity of the underlying synchronization buffer
|
||||
DefaultConcurrentPartialCapacity, // The maximum number of records that can be concurrently accessed
|
||||
// on partial transfer
|
||||
DefaultMirrorBufferFactor, // The maximum fraction of the buffer that should be transfered during
|
||||
// mirroring
|
||||
timeout, // The timeout in time units after which an inactive gRS can be disposed
|
||||
timeoutUnit // The time unit in timeout after which an inactive gRS can be disposed
|
||||
);
|
||||
|
||||
locator = writer.getLocator();
|
||||
|
||||
} catch (GRS2WriterException e) {
|
||||
throw new StreamPublishException("cannot publish stream as resultset", e);
|
||||
}
|
||||
|
||||
Runnable feeder = newFeeder(writer, locator);
|
||||
|
||||
provider.newThread((feeder)).start();
|
||||
|
||||
return locator;
|
||||
}
|
||||
|
||||
// used internally: the task that consumes the stream to publish it in the resultset
|
||||
private Runnable newFeeder(final RecordWriter<Record> writer, final URI locator) {
|
||||
|
||||
return new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
while (stream.hasNext()) {
|
||||
|
||||
try {
|
||||
publishNextElementOrFailure(writer);
|
||||
}
|
||||
//stop publishing
|
||||
catch (RuntimeException e) {
|
||||
|
||||
//also stop consuming if publication was on demand
|
||||
if (onDemand)
|
||||
break;
|
||||
else
|
||||
close(writer,locator); //close as soon as we can
|
||||
}
|
||||
}
|
||||
|
||||
close(writer,locator);
|
||||
stream.close();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void publishNextElementOrFailure(RecordWriter<Record> writer) {
|
||||
|
||||
try {
|
||||
|
||||
try {
|
||||
publish(writer,nextRecord());
|
||||
}
|
||||
catch(StreamSkipSignal skip) {//skip this element and continue
|
||||
return;
|
||||
}
|
||||
catch(StreamStopSignal stop) {//rethrow stop
|
||||
throw stop;
|
||||
}
|
||||
catch(RuntimeException failure) {
|
||||
|
||||
//publish failure
|
||||
publish(writer, failure);
|
||||
|
||||
//stop publishing if cannot be recognised as contingency
|
||||
if (!isContingency(failure))
|
||||
throw failure;
|
||||
}
|
||||
|
||||
}
|
||||
catch(GRS2WriterException failure) {
|
||||
throw new RuntimeException(failure);//stop publishing
|
||||
}
|
||||
}
|
||||
|
||||
private Record nextRecord() {
|
||||
|
||||
try {
|
||||
E element = stream.next();
|
||||
return factory.newRecord(element);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
try {
|
||||
handler.handle(e);
|
||||
}
|
||||
catch(StreamStopSignal stop) {
|
||||
throw e;
|
||||
}
|
||||
throw new StreamSkipSignal();
|
||||
}
|
||||
}
|
||||
|
||||
//private helper: publish a record
|
||||
private void publish(RecordWriter<Record> writer, Record record) throws GRS2WriterException {
|
||||
|
||||
if (writer.getStatus() == Status.Open)
|
||||
if (!writer.put(record, timeout, timeoutUnit)) {
|
||||
log.trace("client is not consuming resulset, stop publishing");
|
||||
throw new GRS2WriterException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//private helper: publish a failure
|
||||
private void publish(RecordWriter<Record> writer, Throwable failure) throws GRS2WriterException {
|
||||
if (writer.getStatus() == Status.Open)
|
||||
if (!writer.put(failure, timeout, timeoutUnit)) {
|
||||
log.trace("client is not consuming resulset, stop publishing");
|
||||
throw new GRS2WriterException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void close(RecordWriter<Record> writer, final URI locator) {
|
||||
|
||||
if (writer.getStatus() == Status.Open) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (GRS2WriterException e) {//log anomaly
|
||||
log.error("error closing resultset at " + locator, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package org.gcube.data.streams.publishers;
|
||||
|
||||
import gr.uoa.di.madgik.grs.record.GenericRecord;
|
||||
import gr.uoa.di.madgik.grs.record.GenericRecordDefinition;
|
||||
import gr.uoa.di.madgik.grs.record.RecordDefinition;
|
||||
import gr.uoa.di.madgik.grs.record.field.Field;
|
||||
import gr.uoa.di.madgik.grs.record.field.FieldDefinition;
|
||||
import gr.uoa.di.madgik.grs.record.field.StringField;
|
||||
import gr.uoa.di.madgik.grs.record.field.StringFieldDefinition;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
|
||||
/**
|
||||
* A {@link RecordFactory} for {@link #STRING_RECORD}s with serialisations of {@link Stream} elements.
|
||||
* <p>
|
||||
* An untyped record is a record with a string-valued <code>payload</code> field.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of the serialised values
|
||||
*/
|
||||
public class RsStringRecordFactory<E> implements RecordFactory<E> {
|
||||
|
||||
/** The type definition of a record with a string-valued <code>payload</code> field. */
|
||||
public static final RecordDefinition STRING_RECORD =
|
||||
new GenericRecordDefinition(new FieldDefinition[]{new StringFieldDefinition("value")});
|
||||
|
||||
private final Generator<E,String> serialiser;
|
||||
|
||||
/**
|
||||
* Creates an instance with a {@link Generator} that returns serialisations of {@link Stream} elements.
|
||||
* @param serialiser the serialiser
|
||||
*/
|
||||
public RsStringRecordFactory(Generator<E,String> serialiser) {
|
||||
this.serialiser=serialiser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericRecord newRecord(E element) {
|
||||
String serialisation = serialiser.yield(element);
|
||||
GenericRecord record = new GenericRecord();
|
||||
record.setFields(new Field[]{new StringField(serialisation)});
|
||||
return record;
|
||||
};
|
||||
|
||||
@Override
|
||||
public RecordDefinition[] definitions() {
|
||||
return new RecordDefinition[]{STRING_RECORD};
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.gcube.data.streams.publishers;
|
||||
|
||||
import gr.uoa.di.madgik.grs.proxy.IWriterProxy;
|
||||
import gr.uoa.di.madgik.grs.proxy.local.LocalWriterProxy;
|
||||
import gr.uoa.di.madgik.grs.proxy.tcp.TCPWriterProxy;
|
||||
|
||||
/**
|
||||
* The transport used by a {@link RsPublisher}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public enum RsTransport {
|
||||
|
||||
TCP() {
|
||||
IWriterProxy proxy() {
|
||||
return new TCPWriterProxy();
|
||||
}
|
||||
},
|
||||
LOCAL {
|
||||
@Override
|
||||
IWriterProxy proxy() {
|
||||
return new LocalWriterProxy();
|
||||
}
|
||||
};
|
||||
|
||||
abstract IWriterProxy proxy();
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.gcube.data.streams.publishers;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* Publishes a {@link Stream} at a given address.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface StreamPublisher {
|
||||
|
||||
/**
|
||||
* Publishes the stream and returns its address.
|
||||
* @return the address
|
||||
* @throws StreamPublishException if the stream cannot be published
|
||||
*/
|
||||
URI publish();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package org.gcube.data.streams.publishers;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* Provides {@link Thread}s for the asynchronous publicaton of {@link Stream}.
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface ThreadProvider {
|
||||
|
||||
/**
|
||||
* Provides a new {@link Thread} in which to execute the publication task.
|
||||
* @param task the task
|
||||
* @return the {@link Thread}
|
||||
*/
|
||||
Thread newThread(Runnable task);
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.gcube.data.streams.test;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
|
||||
/**
|
||||
* An {@link Iterator} that can be staged to throw faults as well as elements. Used for testing purposes.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of stream elements
|
||||
*/
|
||||
public class FallibleIterator<E> implements Iterator<E> {
|
||||
|
||||
final List<? extends Object> elements;
|
||||
final Class<E> clazz;
|
||||
int index;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given type of the elements and given actual elements.
|
||||
*
|
||||
* @param clazz the type of elements
|
||||
* @param elements the actual elements, including {@link RuntimeException}s
|
||||
* @throws IllegalArgumentException if the elements are neither {@link RuntimeException}s nor have the declared type.
|
||||
*/
|
||||
public FallibleIterator(Class<E> clazz, List<? extends Object> elements) throws IllegalArgumentException {
|
||||
this.clazz = clazz;
|
||||
// upfront type checks
|
||||
for (Object e : elements) {
|
||||
try {
|
||||
if (!(e instanceof RuntimeException))
|
||||
clazz.cast(e);
|
||||
} catch (ClassCastException ex) {
|
||||
throw new IllegalArgumentException("invalid stream element: " + e + " is neither a " + clazz.getSimpleName()
|
||||
+ " nor a RuntimeException");
|
||||
}
|
||||
}
|
||||
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (index < elements.size()) {
|
||||
if (elements.get(index) instanceof StreamSkipSignal) {
|
||||
index++;
|
||||
return hasNext();
|
||||
} else
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
|
||||
Object o = elements.get(index);
|
||||
|
||||
index++;
|
||||
|
||||
// throw unchecked as they are
|
||||
if (o instanceof RuntimeException)
|
||||
throw (RuntimeException) o;
|
||||
|
||||
return clazz.cast(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package org.gcube.data.streams.test;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
|
||||
/**
|
||||
* Generates a {@link Stream} for testing purposes.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface StreamProvider {
|
||||
|
||||
/**
|
||||
* Generates a {@link Stream}
|
||||
* @return the stream.
|
||||
*/
|
||||
Stream<?> get();
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package org.gcube.data.streams.test;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.gcube.data.streams.Stream;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
|
||||
/**
|
||||
* Collection of test facilities to validate {@link Stream} implementation and their usage.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the elements of a {@link Stream} as a {@link List}, including failures.
|
||||
* @param stream the stream
|
||||
* @return the elements
|
||||
*/
|
||||
public static List<Object> elementsOf(Stream<?> stream) {
|
||||
|
||||
List<Object> outcomes = new ArrayList<Object>();
|
||||
|
||||
//consume
|
||||
while (stream.hasNext())
|
||||
try {
|
||||
Object e = stream.next();
|
||||
outcomes.add(e);
|
||||
}
|
||||
catch(StreamSkipSignal skip) {
|
||||
continue;
|
||||
}
|
||||
catch(RuntimeException ex) {
|
||||
outcomes.add(ex);
|
||||
}
|
||||
|
||||
stream.close();
|
||||
|
||||
return outcomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a set of sets to test a stream respects the constraints defined by the interface.
|
||||
* @param provider a {@link StreamProvider} that repeatedly provides the stream to test
|
||||
*/
|
||||
public static void validateWith(StreamProvider provider) {
|
||||
|
||||
isAddressableAndClosable(provider.get());
|
||||
canBeIteratedOver(provider.get());
|
||||
respectsCloseSemantics(provider.get());
|
||||
|
||||
}
|
||||
|
||||
//a validation test
|
||||
private static void isAddressableAndClosable(Stream<?> stream) {
|
||||
|
||||
if (stream.locator()==null)
|
||||
throw new AssertionError("locator is null");
|
||||
|
||||
if (stream.isClosed())
|
||||
throw new AssertionError("stream is already closed");
|
||||
|
||||
|
||||
stream.close();
|
||||
|
||||
if (!stream.isClosed())
|
||||
throw new AssertionError("stream has not been closed");
|
||||
}
|
||||
|
||||
//a validation test
|
||||
private static void respectsCloseSemantics(Stream<?> stream) {
|
||||
|
||||
if (stream.isClosed())
|
||||
throw new AssertionError("stream is already closed");
|
||||
|
||||
stream.close();
|
||||
|
||||
if (!stream.isClosed())
|
||||
throw new AssertionError("stream has been closed but does not reveal it");
|
||||
|
||||
if (stream.hasNext())
|
||||
throw new AssertionError("stream indicates that it has elements after being closed");
|
||||
|
||||
|
||||
try {
|
||||
stream.next();
|
||||
throw new AssertionError("stream returns elements after being closed");
|
||||
}
|
||||
catch(NoSuchElementException ex) {
|
||||
//expected
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//a validation test
|
||||
private static void canBeIteratedOver(Stream<?> stream) {
|
||||
|
||||
//can be iterated without hasNext();
|
||||
try {
|
||||
if (stream.next()==null)
|
||||
throw new AssertionError("next() returns null");
|
||||
}
|
||||
catch(NoSuchElementException e) {
|
||||
if (stream.hasNext())
|
||||
throw new AssertionError("stream has no elements but hasNext() returns true");
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
//ignore exception for this test
|
||||
}
|
||||
|
||||
//consume
|
||||
while (stream.hasNext())
|
||||
try {
|
||||
if (stream.next()==null)
|
||||
throw new AssertionError("hasNext() returns true but next() returns null");
|
||||
}
|
||||
catch(RuntimeException e) {
|
||||
//ignore exceptions for this test
|
||||
}
|
||||
|
||||
//hasNext() is idempotent
|
||||
if (stream.hasNext())
|
||||
throw new AssertionError("hasNext() is not idempotent");
|
||||
|
||||
//verify reading past end
|
||||
try {
|
||||
stream.next();
|
||||
throw new AssertionError("stream can be read past its end");
|
||||
}
|
||||
catch(NoSuchElementException e) {
|
||||
//expected
|
||||
}
|
||||
|
||||
stream.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CallbackTest {
|
||||
|
||||
@Test
|
||||
public void callbackConsumeStreams() {
|
||||
|
||||
final List<String> data = asList("1","2","3");
|
||||
|
||||
Stream<String> stream = convert(data);
|
||||
|
||||
final List<String> consumed = new ArrayList<String>();
|
||||
|
||||
Callback<String> callback = new Callback<String>() {
|
||||
|
||||
@Override
|
||||
public void consume(String element) {
|
||||
consumed.add(element);
|
||||
}
|
||||
};
|
||||
|
||||
consume(stream).with(callback);
|
||||
|
||||
assertTrue(stream.isClosed());
|
||||
|
||||
assertEquals(data,consumed);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void callbackStopIteration() {
|
||||
|
||||
final List<String> data = asList("1","2","3");
|
||||
|
||||
Stream<String> stream = convert(data);
|
||||
|
||||
final List<String> consumed = new ArrayList<String>();
|
||||
|
||||
Callback<String> callback = new Callback<String>() {
|
||||
|
||||
@Override
|
||||
public void consume(String element) {
|
||||
if (element.equals("2"))
|
||||
iteration.stop();
|
||||
else
|
||||
consumed.add(element);
|
||||
}
|
||||
};
|
||||
|
||||
consume(stream).with(callback);
|
||||
|
||||
assertTrue(stream.isClosed());
|
||||
|
||||
assertEquals(asList("1"),consumed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbackSkipsElement() {
|
||||
|
||||
final List<String> data = asList("1","2","3");
|
||||
|
||||
Stream<String> stream = convert(data);
|
||||
|
||||
final List<String> consumed = new ArrayList<String>();
|
||||
|
||||
Callback<String> callback = new Callback<String>() {
|
||||
|
||||
@Override
|
||||
public void consume(String element) {
|
||||
if (element.equals("2"))
|
||||
iteration.skip();
|
||||
else
|
||||
consumed.add(element);
|
||||
}
|
||||
};
|
||||
|
||||
consume(stream).with(callback);
|
||||
|
||||
assertTrue(stream.isClosed());
|
||||
|
||||
assertEquals(asList("1","3"),consumed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumerPropagatesFailures() {
|
||||
|
||||
|
||||
Stream<String> stream = stringsAndFaults("1",fault1,"3");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Callback<String> callback = mock(Callback.class);
|
||||
|
||||
try {
|
||||
consume(stream).with(callback);
|
||||
fail();
|
||||
}
|
||||
catch(Exception e) {
|
||||
assertEquals(fault1,e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FoldedStreamTest {
|
||||
|
||||
static int foldStep = 2;
|
||||
|
||||
@Test
|
||||
public void foldedStreamsAreValidStreams() throws Exception {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return fold(convert("1", "2", "3")).in(foldStep);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<? extends Object> folded = asList(asList("1","2"),asList("3"));
|
||||
|
||||
assertEquals(folded, elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foldingHandlesFailures() throws Exception {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return fold(stringsAndFaults(fault1,"1",fault2,"2","3",fault3)).in(foldStep);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
List<? extends Object> folded = asList(fault1,fault2,asList("2", "3"),fault3);
|
||||
|
||||
assertEquals(folded, elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.handlers.CountingHandler;
|
||||
import org.gcube.data.streams.handlers.FaultHandler;
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class GuardedStreamTest {
|
||||
|
||||
static List<String> testData = Arrays.asList("1","2","3");
|
||||
|
||||
static List<Object> testFailingData1 = Arrays.<Object>asList(fault1,"1",fault2,"2",fault3,"3");
|
||||
static List<Object> testFailingData2 = Arrays.<Object>asList(skip,"1",fault2,"2","3",fault3);
|
||||
|
||||
static Generator<String,String> doubler = new Generator<String, String>() {
|
||||
@Override
|
||||
public String yield(String element) {
|
||||
return element+element;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void consumeNoFailures() throws Exception {
|
||||
|
||||
final List<String> data = asList("1","2","3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return guard(convert(data)).with(STOPFAST_POLICY);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(data,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumeIgnoreFailures() {
|
||||
|
||||
final List<? extends Object> data = asList(fault1,"1",fault2,"2",fault3,"3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return guard(stringsAndFaults(data)).with(IGNORE_POLICY);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
List<String> preserved = asList("1","2","3");
|
||||
|
||||
assertEquals(preserved,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumeStopFast() {
|
||||
|
||||
final List<? extends Object> data = asList(fault1,"1",fault2,"2",fault3,"3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return guard(stringsAndFaults(data)).with(STOPFAST_POLICY);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(emptyList(),elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumeStopFastWithSkips() {
|
||||
|
||||
final List<? extends Object> data = asList(skip,"1",fault2,"2","3",fault3);
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return guard(stringsAndFaults(data)).with(STOPFAST_POLICY);
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals(asList("1"),elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumeStopFastCustomPolicy() {
|
||||
|
||||
final FaultHandler customPolicy = new FaultHandler() {
|
||||
|
||||
@Override
|
||||
public void handle(RuntimeException failure) {
|
||||
if (failure==fault2)
|
||||
iteration.stop();
|
||||
}
|
||||
};
|
||||
|
||||
final List<? extends Object> data = asList(fault1,"1",fault2,"2",fault3,"3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return guard(stringsAndFaults(data)).with(customPolicy);
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals(asList("1"),elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumeStopFastcountingPolicy() {
|
||||
|
||||
final FaultHandler customPolicy = new CountingHandler() {
|
||||
|
||||
@Override
|
||||
protected void handle(Exception failure, Exception lastFailure, int failureCount) {
|
||||
if (failureCount>=2)
|
||||
iteration.stop();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
final List<? extends Object> data = asList(fault1,"1",fault2,"2",fault3,"3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return guard(stringsAndFaults(data)).with(customPolicy);
|
||||
}
|
||||
};
|
||||
|
||||
assertEquals(asList("1","2"),elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IteratorStreamTest {
|
||||
|
||||
@Test
|
||||
public void iteratorsMakeValidStreams() {
|
||||
|
||||
final List<String> data = Arrays.asList("1","2","3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return convert(data);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(data,elementsOf(provider.get()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void iteratorsWithFailuresMakeValidStreams() {
|
||||
|
||||
final List<? extends Object> data = asList(fault1,"1",fault2,"2","3",fault3);
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
return convert(data);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(data,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.delegates.StreamListener;
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MonitoredStreamTest {
|
||||
|
||||
static List<String> testData = Arrays.asList("1","2","3");
|
||||
static List<Object> testFailingData = Arrays.<Object>asList(fault1,"1",fault2,"2","3",fault3);
|
||||
|
||||
@Test
|
||||
public void consumeAndListens() throws Exception {
|
||||
|
||||
StreamListener listener = mock(StreamListener.class);
|
||||
|
||||
Stream<String> stream = convert(testData);
|
||||
stream = monitor(stream).with(listener);
|
||||
|
||||
//just consume stream
|
||||
elementsOf(stream);
|
||||
|
||||
verify(listener).onStart();
|
||||
verify(listener).onEnd();
|
||||
verify(listener,times(2)).onClose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consume() throws Exception {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = convert(testData);
|
||||
return monitor(stream).with(mock(StreamListener.class));
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(testData, elementsOf(provider.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleFailure() throws Exception {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = convert(stringsAndFaults(testFailingData));
|
||||
return monitor(stream).with(mock(StreamListener.class));
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(testFailingData, elementsOf(provider.get()));
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PipedStreamTest {
|
||||
|
||||
static Generator<String,String> doubler = new Generator<String, String>() {
|
||||
@Override
|
||||
public String yield(String element) {
|
||||
return element+element;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void pipesYieldValidStreams() throws Exception {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = convert("1","2","3");
|
||||
return pipe(stream).through(doubler);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
List<String> piped = asList("11","22","33");
|
||||
|
||||
assertEquals(piped,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pipesHandleFailures() {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = stringsAndFaults(fault1,"1",fault2,"2","3",fault3);
|
||||
return pipe(stream).through(doubler);
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
List<? extends Object> piped = asList(fault1,"11",fault2,"22","33",fault3);
|
||||
|
||||
assertEquals(piped,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pipesHandleSkipSignals() {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = stringsAndFaults(skip,"1",skip,"2","3",skip);
|
||||
return pipe(stream).through(doubler);
|
||||
}
|
||||
};
|
||||
|
||||
List<String> piped = asList("11","22","33");
|
||||
|
||||
assertEquals(piped,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pipesHandleStopSignals() {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = stringsAndFaults("1","2",stop,"3");
|
||||
return pipe(stream).through(doubler);
|
||||
}
|
||||
};
|
||||
|
||||
List<String> piped = asList("11","22");
|
||||
|
||||
assertEquals(piped,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorsCanSkipElements() {
|
||||
|
||||
final Generator<String,String> generator = new Generator<String, String>() {
|
||||
@Override
|
||||
public String yield(String element) {
|
||||
if (element.equals("2"))
|
||||
iteration.skip();
|
||||
return element+element;
|
||||
}
|
||||
};
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = stringsAndFaults("1","2","3");
|
||||
return pipe(stream).through(generator);
|
||||
}
|
||||
};
|
||||
|
||||
List<String> piped = asList("11","33");
|
||||
|
||||
assertEquals(piped,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorsCanStopIteration() {
|
||||
|
||||
final Generator<String,String> generator = new Generator<String, String>() {
|
||||
@Override
|
||||
public String yield(String element) {
|
||||
if (element.equals("2"))
|
||||
iteration.stop();
|
||||
return element+element;
|
||||
}
|
||||
};
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = stringsAndFaults("1","2","3");
|
||||
return pipe(stream).through(generator);
|
||||
}
|
||||
};
|
||||
|
||||
List<String> piped = asList("11");
|
||||
|
||||
assertEquals(piped,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PublishTest {
|
||||
|
||||
static List<String> testData = Arrays.asList("1","2","3","4","5");
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
|
||||
System.setProperty("org.slf4j.simplelogger.defaultlog", "trace");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishAndRead() {
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = convert(testData);
|
||||
URI rs = publishStringsIn(stream).withDefaults();
|
||||
return convert(rs).ofStrings().withDefaults();
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(testData,elementsOf(provider.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishOnDemand() throws Exception {
|
||||
|
||||
final List<Boolean> consumed = new ArrayList<Boolean>();
|
||||
|
||||
final Generator<String,String> consumer = new Generator<String, String>() {
|
||||
@Override
|
||||
public String yield(String element) {
|
||||
consumed.add(true);
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
Stream<String> stream = convert(testData);
|
||||
stream = pipe(stream).through(consumer);
|
||||
|
||||
URI rs = publishStringsIn(stream).withBufferOf(2).withDefaults();
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
int generated = consumed.size();
|
||||
assertTrue("only some elements have been moved",generated<testData.size());
|
||||
|
||||
stream = convert(rs).ofStrings().withDefaults();
|
||||
|
||||
|
||||
|
||||
assertTrue("no more elements have been moved",generated == consumed.size());
|
||||
|
||||
//pull some
|
||||
while (stream.hasNext())
|
||||
stream.next();
|
||||
|
||||
assertTrue("some more elements have moved",generated <consumed.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishContinuously() throws Exception {
|
||||
|
||||
final List<Boolean> consumed = new ArrayList<Boolean>();
|
||||
|
||||
final Generator<String,String> consumer = new Generator<String, String>() {
|
||||
@Override
|
||||
public String yield(String element) {
|
||||
consumed.add(true);
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
Stream<String> stream = convert(testData);
|
||||
stream = pipe(stream).through(consumer);
|
||||
|
||||
publishStringsIn(stream).withBufferOf(2).withTimeoutOf(100,TimeUnit.MILLISECONDS).nonstop().withDefaults();
|
||||
|
||||
//wait for longer than timeout to trigger continuous publication
|
||||
Thread.sleep(200);
|
||||
|
||||
assertEquals("all elements have been consumed",testData.size(),consumed.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishWithConfiguration() throws Exception {
|
||||
|
||||
List<String> elements = asList("1","2");
|
||||
|
||||
Stream<String> stream = convert(elements);
|
||||
|
||||
URI resultset = publishStringsIn(stream).nonstop().withBufferOf(10).withTimeoutOf(1,TimeUnit.HOURS).withDefaults();
|
||||
|
||||
Stream<String> published = convert(resultset).ofStrings().withDefaults();
|
||||
|
||||
int i=0;
|
||||
while (published.hasNext()) {
|
||||
assertEquals(elements.get(i),published.next());
|
||||
i++;
|
||||
}
|
||||
|
||||
assertEquals(2,i);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contingenciesArePublished() {
|
||||
|
||||
List<? extends Object> data = asList(contingency1,"1",contingency2,"2","3",contingency3);
|
||||
|
||||
Stream<String> stream = stringsAndFaults(data);
|
||||
|
||||
URI rs = publishStringsIn(stream).withDefaults();
|
||||
|
||||
Stream<String> published = convert(rs).ofStrings().withDefaults();
|
||||
|
||||
List<Object> elements = elementsOf(published);
|
||||
|
||||
System.out.println(elements);
|
||||
|
||||
for (int i = 0; i<data.size();i++)
|
||||
if (elements.get(i) instanceof Exception) {
|
||||
Exception read = (Exception) elements.get(i);
|
||||
assertTrue(data.get(i) instanceof Exception);
|
||||
Exception original = (Exception) data.get(i);
|
||||
//causes are preserved
|
||||
assertEquals(original.getCause().getClass(),read.getCause().getClass());
|
||||
}
|
||||
else
|
||||
assertEquals(data.get(i),elements.get(i));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstOutageIsPublished() {
|
||||
|
||||
List<? extends Object> segmentUntilOutage = asList("1",fault1);
|
||||
List<Object> data = new ArrayList<Object>(segmentUntilOutage);
|
||||
data.add("2");
|
||||
|
||||
Stream<String> stream = stringsAndFaults(data);
|
||||
|
||||
URI rs = publishStringsIn(stream).withDefaults();
|
||||
|
||||
Stream<String> published = convert(rs).ofStrings().withDefaults();
|
||||
|
||||
List<Object> elements = elementsOf(published);
|
||||
|
||||
//resultset stops at first outage
|
||||
assertEquals(elements.size(),segmentUntilOutage.size());
|
||||
|
||||
for (int i = 0; i<elements.size();i++)
|
||||
if (elements.get(i) instanceof Exception)
|
||||
assertTrue(data.get(i) instanceof Exception);
|
||||
else
|
||||
assertEquals(data.get(i),elements.get(i));
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamOpenException;
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RsStreamTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
|
||||
System.setProperty("org.slf4j.simplelogger.defaultlog", "trace");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resultsetsMakeValidStreams() throws Exception {
|
||||
|
||||
final List<String> elements = asList("1","2","3");
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
|
||||
@Override
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = convert(elements);
|
||||
URI resultset = publishStringsIn(stream).withDefaults();
|
||||
return convert(resultset).ofStrings().withDefaults();
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
assertEquals(elements,elementsOf(provider.get()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void resultsetsWithFailuresMakeValidStreams() throws Exception {
|
||||
|
||||
RuntimeException fault = new RuntimeException(new TestContingency());
|
||||
|
||||
final List<? extends Object> elements = asList(fault,"1",fault,"2","3",fault);
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
|
||||
@Override
|
||||
public Stream<?> get() {
|
||||
Stream<String> stream = stringsAndFaults(elements);
|
||||
URI resultset = publishStringsIn(stream).withDefaults();
|
||||
return convert(resultset).ofStrings().withDefaults();
|
||||
}
|
||||
};
|
||||
|
||||
validateWith(provider);
|
||||
|
||||
//exceptions instances will be different
|
||||
assertEquals(elements.size(),elementsOf(provider.get()).size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void resultsetCannotBeOpened() throws Exception {
|
||||
|
||||
//publish mock stream
|
||||
URI resultset = URI.create("tcp://malformed");
|
||||
|
||||
//stream resultset
|
||||
Stream<String> stream =convert(resultset).ofStrings().withDefaults();
|
||||
|
||||
try {
|
||||
stream.next();
|
||||
fail();
|
||||
}
|
||||
catch(StreamOpenException e) {}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import org.gcube.data.streams.exceptions.StreamContingency;
|
||||
|
||||
@StreamContingency
|
||||
public class TestContingency extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.dsl.Streams;
|
||||
import org.gcube.data.streams.exceptions.StreamException;
|
||||
import org.gcube.data.streams.exceptions.StreamSkipSignal;
|
||||
import org.gcube.data.streams.exceptions.StreamStopSignal;
|
||||
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
|
||||
static RuntimeException fault1 = new RuntimeException();
|
||||
static RuntimeException fault2 = new RuntimeException();
|
||||
static RuntimeException fault3 = new RuntimeException();
|
||||
static StreamSkipSignal skip = new StreamSkipSignal();
|
||||
static StreamStopSignal stop = new StreamStopSignal();
|
||||
static StreamException contingency1 = new StreamException(new TestContingency());
|
||||
static StreamException contingency2 = new StreamException(new TestContingency());
|
||||
static StreamException contingency3 = new StreamException(new TestContingency());
|
||||
|
||||
|
||||
static Stream<String> stringsAndFaults(Object ... elements) {
|
||||
return Streams.convertWithFaults(String.class, elements);
|
||||
}
|
||||
|
||||
static Stream<String> stringsAndFaults(List<? extends Object> elements) {
|
||||
return Streams.convertWithFaults(String.class, elements);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package org.gcube.data.streams;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.gcube.data.streams.TestUtils.*;
|
||||
import static org.gcube.data.streams.dsl.Streams.*;
|
||||
import static org.gcube.data.streams.test.Utils.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.data.streams.dsl.Streams;
|
||||
import org.gcube.data.streams.generators.Generator;
|
||||
import org.gcube.data.streams.test.StreamProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UnfoldedStreamTest {
|
||||
|
||||
static Generator<List<String>, Stream<String>> streamer = new Generator<List<String>, Stream<String>>() {
|
||||
@Override
|
||||
public Stream<String> yield(List<String> element) {
|
||||
return convert(element);
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void consume() throws Exception {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<List<String>> data = asList(asList("1","2","3"),asList("1","2","3"));
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<List<String>> stream = convert(data);
|
||||
return unfold(stream).through(streamer);
|
||||
}
|
||||
};
|
||||
|
||||
List<String> unfoldedData = asList("1","2","3","1","2","3");
|
||||
|
||||
assertEquals(unfoldedData,elementsOf(provider.get()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleFailures() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<? extends Object> data = asList(asList(fault1,"1",fault2,"2","3",fault3),
|
||||
asList("1",fault1,"2",fault2,"3"));
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
Stream<List<String>> stream = (Stream) Streams.convertWithFaults(List.class,data);
|
||||
return unfold(stream).through(streamer);
|
||||
}
|
||||
};
|
||||
|
||||
List<? extends Object> unfoldedFailingData = asList(fault1,"1",fault2,"2","3",fault3,"1",fault1,"2",fault2,"3");
|
||||
|
||||
assertEquals(unfoldedFailingData,elementsOf(provider.get()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreFailures() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<List<String>> data = asList(asList("1","2","3"),asList("1","2","3"));
|
||||
|
||||
StreamProvider provider = new StreamProvider() {
|
||||
public Stream<?> get() {
|
||||
Stream<List<String>> stream = convert(data);
|
||||
Stream<String> unfolded = unfold(stream).through(streamer);
|
||||
return guard(unfolded).with(IGNORE_POLICY);
|
||||
}
|
||||
};
|
||||
|
||||
List<String> unfoldedData = asList("1","2","3","1","2","3");
|
||||
|
||||
assertEquals(unfoldedData,elementsOf(provider.get()));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue