JAM - examples
We saw lots of basic examples how to publish with JAM in terms of announcing texts, messages and arbitrary objects asynchronously. JAM supports synchronous information flows as well, which means we get an immediate response, like from an invocation of any other operation which has a return type (that is not void). With such an invocation we rather demand data than offering data (even if we also offer some data needed to process the demand). Therefore a synchronous announcement is called a Demand and a subscriber returning the demanded data (or null) is called a Supplier.
We can find in the publisher API of JAM like Publish, PublishParallel, Composition, ParallelComposition, etc. many operations to demand data beside the operations to offer data. A typical example for a Demand is to demand an instance of a given type:
Long long = Publish.demandInstance(Long.class).poll();
More than one Supplier might return an instance, so we get back a queue and we simply poll. To receive the InstanceDemand we subscribe a Supplier implementing the InstanceSupplier interface same like we did with the Consumers, but this implementation can return an object (here a Long). Again, in a real-world scenario we will demand a more specific data type and not just Long. But even with a more specific data type the demand might be still too unspecific in terms of how a Supplier shall satisfy the demand, e.g. delivering specific data within the returned object. For being able to provide an exact specification of what data we demand from Suppliers, JAM provides a specification API that can be used in addition with many publishing operations, for demands and offers.
Long long = Publish.demandInstance(Long.class, new NotNullSpecification<Long>(), true).poll();
Here, we require to get back an object and not null. Even if some Suppliers may return null, at least one Supplier must return a valid object as specified, otherwise JAM will throw a SpecificationUnsatisfiedException. Additionally to "not null" we can create Specifications to validate domain and use case specific data. To do so we extend usually the SpecificationDecoratorBase or the SpecificationChainBase, to decorate and / or chain specifications with each other.
Announcements and Subscribers which support Specifications are segregated from those without Specifications. In our example we receive an SpecifiedInstanceDemand with an implementation of the InstanceSpecificationSupplier interface. SpecificationSuppliers can use a specified demand to test objects against the specification, e.g. to filter a collection, before returning them.
If it comes to use Specifications for Offers, we do not specify objects to return, of course, but we specify a compliance that implements the interface Compliance like e.g. ComplianceState. Imagine for instance the Consumer which receives a specified offer is a batch process implementation. Then we probably want to assert the batch process has been completed without failures or with some tolerated failures or partially completed etc.
When using the static methods of Publish or PublishParallel, its easy for simple cases, but if we want to do more complex stuff that kind of API may feel too clumsy. Therefore JAM provides fluent API, which are more or less self-explaining and provide way more flexibility and even possibilities beyond Publish or PublishParallel, but feel inflated for very simple cases. We invoke the fluent API like this (using Long again)...