3. Consumers - aegisql/conveyor GitHub Wiki

Table of Contents

Consumers

Conveyor Framework provides two extensions to standard Java8 Consumer functional interface

@FunctionalInterface
public interface ResultConsumer <K,V> extends Consumer<ProductBin<K,V>>{
	default ResultConsumer<K,V> andThen(ResultConsumer <K,V> other)	
	default ResultConsumer<K,V> filter(Predicate<ProductBin<K,V>> filter)
	default ResultConsumer<K,V> filterKey(Predicate<K> filter)
	default ResultConsumer<K,V> filterResult(Predicate<V> filter)
	default ResultConsumer<K,V> filterStatus(Predicate<Status> filter)
	default ResultConsumer<K,V> async(ExecutorService pool)
	default ResultConsumer<K,V> async()
}
@FunctionalInterface
public interface ScrapConsumer<K,V> extends Consumer<ScrapBin<K,V>> {
	default ScrapConsumer<K,V> andThen(ScrapConsumer<K,V> other)
	default ScrapConsumer<K,V> filter(Predicate<ScrapBin<K,V>> filter)
	default ScrapConsumer<K,V> filterKey(Predicate<K> filter)
	default ScrapConsumer<K,V> filterScrap(Predicate<V> filter)
	default ScrapConsumer<K,V> filterScrapType(Predicate<Class<?>> filter)
	default ScrapConsumer<K,V> filterFailureType(Predicate<FailureType> filter)
	default ScrapConsumer<K,V> filterError(Predicate<Throwable> filter)
	default ScrapConsumer<K,V> async(ExecutorService pool)
	default ScrapConsumer<K,V> async()
}

Default Methods

andThen - andThen allows to build a chain of consumers.

ResultConsumer<Integer, String> rc = bin->{
	System.out.println("key="+bin.key);
};
rc = rc.andThen(bin->{
	System.out.println("product="+bin.product);
});

Filters - filters allow to apply some criteria to the consuming act. Consumption only happens when the Predicate, passed as a filter parameter, returns true. Most common filtering method filter(binPredicate) works with Product or Scrap bin container. Other filters work with certain bin fields, e.g. filterKey(keyPredicate) allows to filter objects by their keys.

rc = rc.filterStatus(status->status.equals(Status.READY));

async - these two methods allow to execute the consumer asynchronously. Either in default thread pool, or using Executors provided by a client explicitly. Please note, that pool's submit(...) methods returns immediately and Product Futures will return result as soon as the last Consumer in the chain is done. If async Consumer crash or stuck, this will not affect Futures produced by the conveyor. This is a powerful feature, but you need yo use it wisely.

rc = rc.async();

Nonblocking Consumers

The com.aegisql.conveyor.consumers package also contains number of nonblocking consumers that can be used as is, as an example, or as a part of customized solutions. Here is a list of available consumers:

  • IgnoreResult/IgnoreScrap - Does nothing with the result or scrap. It can be used when you synchronize with your product via product's Future, or as a placeholder for testing or debugging purposes.
  • LogResult/LogScrap - simple logger for the result. It relies on the toString method of Product and Scrap bins, thus, on implementation of the toString() method you providing with your Product class or with its building parts. You can chose among available logging levels. Default logger writes to the com.aegisql.conveyor.Conveyor log, but you can chose your own logger.
LogResult<Integer,User> loggingResultConsumer = LogResult.info(conveyor,User.class);
LogScrap<Integer> loggingScrapConsumer = LogScrap.error(conveyor);
LastResultReference<String,User> lastResult = LastResultReference.of(conveyor);
conveyor.resultConsumer().first(lastResult).set();
...
User user = lastResult.getCurrent();
  • LastResults/LastScraps - Same as last reference, but keeps last N results or errors in a circle collection, in order they happened.
LastResults<String,User> lastResults = LastResults.of(conveyor,10); //keep 10 last results
conveyor.resultConsumer().first(lastResults).set();
...
List<User> last = lastResults.getLast();
  • FirstResults/FirstScraps - Same as Last results, but keeps first N results or errors.
  • ResultCounter/ScrapCounter - keeps a growing counter for results or errors. In combination with filters allows to collect statistics on events of certain types.
ResultCounter<String,User> timeoutCounter = ResultCounter
   .of(conveyor)
   .filterStatus(status->status.equals(Status.TIMED_OUT)); 
conveyor.resultConsumer().andThen(timeoutCounter).set();
...
long timeouts = timeoutCounter.get();

Loaders

Loaders allow to manipulate Result and Scrap consumers in a safe way. They immutable and have no side effects.

ResultConsumerLoader

You almost never should instantiate the ResultConsumerLoader directly, instead, you call the conveyor's method.

ResultConsumerLoader<Integer,User> rcl = conveyor.resultConsumer();
//OR
ResultConsumerLoader<Integer,User> rcl = conveyor.resultConsumer(resultConsumer);

First method returns an instance which contains a reference to a current default result consumer. The second - seeds the loader with provided result consumer. When you obtained and instance of the loader, you can call its methods to change result consumption behavior. The last method you call is set(). Only then final ResultConsumer is built and applied to the conveyor.

  • first(resultConsumer) - this method replaces current ResultConsumer in the loader. Usually called once.
  • andThen(resultConsumer) - links new result consumer to the current. Can be called as many time, as needed.
  • set() - This method, finally, applies Result consumer to the conveyor.

Build-specific parameters ResultConsumerLoader has a unique feature - it can be separately customized and applied to a certain build or group of existing builds, identified by their keys.

  • id(key) - Tells the loader, that this result consumer must be applied to the build, instead of the default consumer.
  • foreach() - apply created result consumer to all existing builds
  • foreach(keyPredicate) - apply created result consumer to builds with keys that match the filter.

Note that method set() returns a CompletableFuture, as any other building part loader. So, you can trace that ResultConsumer were assigned to the build, and can be sure that it will be applied when the build is ready.

ScrapConsumerLoader

Similar to the ResultConsumerLoader, but for now it implementation does not allow to customize scrap consumer for individual builds. This is caused by the fact, that some errors can happen even before any build were created.

Some examples

Build with ID=1 will be printed to STDOUT (Default result consumer) and build with ID=2 - to STDERR.

AssemblingConveyor<Integer, UserBuilderEvents, User> c = new AssemblingConveyor<>();
...
c.resultConsumer().first(LogResult.stdOut(c)).set();
c.setReadinessEvaluator(Conveyor
   .getTesterFor(c)
   .accepted(UserBuilderEvents.SET_FIRST,UserBuilderEvents.SET_LAST,UserBuilderEvents.SET_YEAR));
...		
c.part().id(1).label(UserBuilderEvents.SET_FIRST).value("A").place();
c.part().id(2).label(UserBuilderEvents.SET_FIRST).value("A2").place();

c.resultConsumer().foreach(k->k%2==0).first(LogResult.stdErr(c)).set();

c.part().id(1).label(UserBuilderEvents.SET_LAST).value("B").place();
c.part().id(2).label(UserBuilderEvents.SET_LAST).value("B2").place();
c.part().id(1).label(UserBuilderEvents.SET_YEAR).value(2017).place();
c.part().id(2).label(UserBuilderEvents.SET_YEAR).value(2007).place();
⚠️ **GitHub.com Fallback** ⚠️