Introduction
This project is a collection of miscellaneous tools shared by a number of projects of the same author. There are simple tuples to use with Java Streams, type-safe maps (inspired by the heterogeneous map pattern) described in Effective Java by Joshua Bloch, a finder that handles in a smart way queries to data sources, a facility to use the DCI (Data, Context and Interactions) architectural pattern, a simple message bus suitable for using the pub-sub pattern inside an application, some test utilities, an experimental actor framework and a few other small things.
The project name is a tribute to the jazz standard with the same name by Maschwitz and Strachey.
The project is composed of mostly independent modules, with different levels of stability and internal quality. At the moment there is not an automatic system to check the compatibility of the APIs from version to version; in general look at the following Javadoc tags:
- stable: to designate things with a certain degree of maturity.
- draft: to designate things that are going to stabilise;
- experimental: to designate things that are really raw and might go away soon, or perhaps mutate dramatically;
TheseFoolishThings is licensed with the Apache license.
Table of contents
A quick look
Here it is a quick look at some of the available features.
Pair
and Triple
Pair
and Triple
are implementations of 2 and 3-elements tuples. They are pretty useful in Stream
programming, for instance offering a way to
implement nested loops:
final var actual = IntStream.rangeClosed(1, limit)
.boxed()
.flatMap(a -> Pair.pairRangeClosed(a, a + 1, limit))
.flatMap(p -> Triple.tripleRangeClosed(p, p.b + 1, limit))
.collect(toList());
… or indexed loops:
final var actual = IntStream.rangeClosed(1, limit)
.boxed()
.flatMap(a -> Pair.pairRangeClosed(a, a + 1, limit))
.collect(toList());
It is also possible to “zip” two streams into a Stream<Pair>
:
// given
final var intStream = IntStream.range(0, 5).boxed();
final var stringStream = IntStream.range(0, 5).mapToObj(n -> "string-" + (char)('a' + n));
// when
final var underTest = Pair.zip(intStream, stringStream);
// then
assertThat(underTest.collect(toList()), is(asList(Pair.of(0, "string-a"),
Pair.of(1, "string-b"),
Pair.of(2, "string-c"),
Pair.of(3, "string-d"),
Pair.of(4, "string-e"))));
Find more information in the documentation for module Utilities.
Finder
Finder
is a query builder with a fluent API that allows to access collections of data from a variety of data sources. It offers features such as pagination
and composition. Its API is extensible, so a variety of filtering criteria and query parameters can be specified. Notwithstanding the apparent similarity
with Stream
s, it is quite different since it supports not only in-memory operations; for instance, implementations can query a database by generating ad-hoc
SQL, so data are retrieved in the most efficient way.
log.info("Whose first name starts with B: {}",
registry.findPerson()
.withFirstName("B.*")
.results());
log.info("Whose first name starts with B, sorted by first name: {}",
registry.findPerson()
.sort(BY_FIRST_NAME)
.withFirstName("B.*")
.results());
Find more information in the documentation for module Utilities.
As
As
provides a dynamic implementation of the Data, Context and Interaction architectural pattern — dynamic in the sense that classes providing interaction
support can be associated to existing objects. For instance, in the following code Displayable
and Renderable
are not known to the model class Person
,
but they are available at runtime in association with that class to provide extended functions:
@ExtensionMethod(AsExtensions.class) @Slf4j
public class DisplayableExample
{
public void run()
{
final var joe = new Person(new Id("1"), "Joe", "Smith");
final var luke = new Person(new Id("2"), "Luke", "Skywalker");
// approach with classic getter
log.info("******** (joe as Displayable).displayName: {}", joe.as(_Displayable_).getDisplayName());
log.info("******** (luke as Displayable).displayName: {}", luke.as(_Displayable_).getDisplayName());
// approach oriented to Tell Don't Ask
joe.as(_Renderable_).renderTo("******** (joe as Renderable): %1$s %2$s ", log::info);
luke.as(_Renderable_).renderTo("******** (luke as Renderable): %1$s %2$s ", log::info);
}
}
Find more information in the documentation for module Utilities.
P&S MessageBus
PENDING INTRODUCTION AND EXAMPLE
Find more information in the documentation for module MessageBus.
Collaborative Actors
PENDING INTRODUCTION AND EXAMPLE
Find more information in the documentation for module Actors.
Type-safe maps
Inspired by the heterogeneous map pattern described in Effective Java by Joshua Bloch,
TypeSafeMap
is an immutable map that works with type-aware keys, so its retrieval method is type-safe; furthermore it supports Optional
.
final Key<String> k1 = Key.of("Key 1", String.class);
final Key<Integer> k2 = Key.of("Key 2", Integer.class);
final var m = TypeSafeMap.newInstance()
.with(k1, "Value 1")
.with(k2, 1);
final Optional<String> v1 = m.getOptional(k1);
final Optional<Integer> v2 = m.getOptional(k2);
assertThat(v1.get(), is("Value 1"));
assertThat(v2.get(), is(1));
TypeSafeMultiMap
is similar, but associates keys to collection of values, not single values; so associating multiple (key, value)
pairs keep
all the values instead of replacing the previous one.
final Key<String> k1 = Key.of("Key 1", String.class);
final Key<Integer> k2 = Key.of("Key 2", Integer.class);
final var m = TypeSafeMultiMap.newInstance()
.with(k1, "Value 1")
.with(k1, "Value 2")
.with(k2, 1)
.with(k2, 2);
final Collection<String> v1 = m.get(k1);
final Collection<Integer> v2 = m.get(k2);
assertThat(v1, is(List.of("Value 1", "Value 2")));
assertThat(v2, is(List.of(1, 2)));
Find more information in the documentation for module Utilities.
Modules
General information
Maven dependency
Modules can be used independently, so each needed one must be included separately. The master POM of the project
can be used as a BOM and included in the dependencyManagement
section:
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>it.tidalwave.thesefoolishthings</groupId>
<artifactId>thesefoolishthings</artifactId>
<version>3.2-ALPHA-24</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencies>
</dependencyManagement>
Sources, issue tracker and continuous integration
The primary source repository is on Bitbucket, a secondary repository (synchronized in real time) is available on GitHub.
To checkout sources from Bitbucket:
> git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
To checkout sources from GitHub:
> git clone https://github.com/tidalwave-it/thesefoolishthings-src
The issue tracker is hosted on the Atlassian Jira Cloud:
The continuous integration is available at:
- Tidalwave CI server (primary): http://services.tidalwave.it/ci/view/TheseFoolishThings
- Travis: https://app.travis-ci.com/bitbucket/tidalwave/thesefoolishthings-src
- Bitbucket pipelines (demonstration only): https://bitbucket.org/tidalwave/thesefoolishthings-src/addon/pipelines/home