== Functional Map API

Infinispan 8 introduces a new experimental API for interacting with your
data which takes advantage of the functional programming additions and
improved asynchronous programming capabilities available in Java 8.

Infinispan's link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.html[Functional Map API]
is a distilled map-like asynchronous API which uses functions to interact with data.

=== Asynchronous and Lazy

Being an asynchronous API, all methods that return a single result,
return a CompletableFuture which wraps the result, so you can use the
resources of your system more efficiently by having the possibility to
receive callbacks when the
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html[`CompletableFuture`]
has completed, or you can chain or compose them with other CompletableFuture.

For those operations that return multiple results, the API returns
instances of a
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Traversable.html[`​Traversable`]
interface which offers a lazy pull-style
API for working with multiple results.
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Traversable.html[`​Traversable`]
,​ being a lazy pull-style API, can still be asynchronous underneath
since the user can decide to work on the traversable at a later stage,
and the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Traversable.html[`​Traversable`]
implementation itself can decide when to compute
those results.

=== Function transparency

Since the content of the functions is transparent to Infinispan, the API
has been split into 3 interfaces for read­-only (
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadOnlyMap.html[`R​eadOnlyMap`]
)​, read­-write (
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadWriteMap.html[`R​eadWriteMap`]
)​ and write­-only (
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html[`W​riteOnlyMap`]
)​ operations respectively, in order to provide hints to the Infinispan
internals on the type of work needed to support functions.

=== Constructing Functional Maps

To construct any of the read-only, write-only or read-write map
instances, an Infinispan
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/AdvancedCache.html[`AdvancedCache`]
is required, which is retrieved from the Cache Manager, and using the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/AdvancedCache.html[`AdvancedCache`]
, static method
factory methods are used to create
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadOnlyMap.html[`R​eadOnlyMap`]
,
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadWriteMap.html[`R​eadWriteMap`]
or
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html[`W​riteOnlyMap`]
:

[source,java]
----
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.functional.impl.*;

AdvancedCache<String, String> cache = ...

FunctionalMapImpl<String, String> functionalMap = FunctionalMapImpl.create(cache);
ReadOnlyMap<String, String> readOnlyMap = ReadOnlyMapImpl.create(functionalMap);
WriteOnlyMap<String, String> writeOnlyMap = WriteOnlyMapImpl.create(functionalMap);
ReadWriteMap<String, String> readWriteMap = ReadWriteMapImpl.create(functionalMap);
----

WARNING: At this stage, the Functional Map API is experimental and hence the
way FunctionalMap, ReadOnlyMap, WriteOnlyMap and ReadWriteMap are constructed
is temporary.

=== Read-Only Map API

Read-only operations have the advantage that no locks are acquired
for the duration of the operation. Here's an example on how to the
equivalent operation for
link:https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#get-java.lang.Object-[Map.get(K)]
:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;

ReadOnlyMap<String, String> readOnlyMap = ...
CompletableFuture<Optional<String>> readFuture = readOnlyMap.eval("key1", ReadEntryView::find);
readFuture.thenAccept(System.out::println);
----

Read-only map also exposes operations to retrieve multiple keys in one go:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.api.functional.Traversable;

ReadOnlyMap<String, String> readOnlyMap = ...

Set<String> keys = new HashSet<>(Arrays.asList("key1", "key2"));
Traversable<String> values = readOnlyMap.evalMany(keys, ReadEntryView::get);
values.forEach(System.out::println);
----

Finally, read-only map also exposes methods to read all existing keys as well
as entries, which include both key and value information.

[[_read_only_entry_view]]
==== Read-Only Entry View

The function parameters for read-only maps provide the user with a
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.ReadEntryView.html[read-only entry view]
to interact with the data in the cache, which include these operations:

* link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.ReadEntryView.html#key--[`key()`]
method returns the key for which this function is being executed.
* link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.ReadEntryView.html#find--[`find()`]
returns a Java 8 `Optional` wrapping the value if present,
otherwise it returns an empty optional. Unless the value is guaranteed to
be associated with the key, it's recommended to use `find()` to verify
whether there's a value associated with the key.
* link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.ReadEntryView.html#get--[`get()`]
returns the value associated with the key. If the key has no value
associated with it, calling `get()` throws a `NoSuchElementException`.
`get()` can be considered as a shortcut of `ReadEntryView.find().get()`
which should be used only when the caller has guarantees that there's
definitely a value associated with the key.
* link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/MetaParam.Lookup.html#findMetaParam-java.lang.Class-[`findMetaParam(Class<T> type)`]
allows metadata parameter information
associated with the cache entry to be looked up, for example: entry
lifespan, last  accessed time...etc.
See <<_meta_parameter, Metadata Parameter Handling section>> to find out more.

=== Write-Only Map API

Write-only operations include operations that insert or update data in the
cache and also removals. Crucially, a write-only operation does not attempt
to read any previous value associated with the key. This is an important
optimization since that means neither the cluster nor any persistence stores
will be looked up to retrieve previous values. In the main Infinispan Cache,
this kind of optimization was achieved using a local-only per-invocation
flag, but the use case is so common that in this new functional API, this
optimization is provided as a first-class citizen.

Using
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html[write-only map API]
, an operation equivalent to
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java[`javax.cache.Cache` (`JCache`)]
's void returning
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java#L194[`put`]
can be achieved this way, followed by an attempt to read the stored
value using the read-only map API:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;

WriteOnlyMap<String, String> writeOnlyMap = ...
ReadOnlyMap<String, String> readOnlyMap = ...

CompletableFuture<Void> writeFuture = writeOnlyMap.eval("key1", "value1",
   (v, view) -> view.set(v));
CompletableFuture<String> readFuture = writeFuture.thenCompose(r ->
   readOnlyMap.eval("key1", ReadEntryView::get));
readFuture.thenAccept(System.out::println);
----

Multiple key/value pairs can be stored in one go using
https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html#evalMany-java.util.Map-java.util.function.BiConsumer-[`evalMany`]
API:

[source,java]
----
WriteOnlyMap<String, String> writeOnlyMap = ...

Map<K, String> data = new HashMap<>();
data.put("key1", "value1");
data.put("key2", "value2");
CompletableFuture<Void> writerAllFuture = writeOnlyMap.evalMany(data, (v, view) -> view.set(v));
writerAllFuture.thenAccept(x -> "Write completed");
----

To remove all contents of the cache, there are two possibilities with
different semantics. If using
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html#evalAll-java.util.function.Consumer-[`evalAll`]
each cached entry is iterated over and the function is called
with that entry's information. Using this method also results in listeners
(see <<_functional_listeners, functional listeners section>> for more information)
being invoked:

[source,java]
----
WriteOnlyMap<String, String> writeOnlyMap = ...

CompletableFuture<Void> removeAllFuture = writeOnlyMap.evalAll(WriteEntryView::remove);
removeAllFuture.thenAccept(x -> "All entries removed");
----

The alternative way to remove all entries is to call
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html#truncate--[`truncate`]
operation which clears the entire cache contents in one go without
invoking any listeners and is best-effort:

[source,java]
----
WriteOnlyMap<String, String> writeOnlyMap = ...

CompletableFuture<Void> truncateFuture = writeOnlyMap.truncate();
truncateFuture.thenAccept(x -> "Cache contents cleared");
----

[[_write_only_entry_view]]
==== Write-Only Entry View
The function parameters for write-only maps provide the user with a
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.WriteEntryView.html[write-only entry view]
to modify the data in the cache, which include these
operations:

* link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.WriteEntryView.html#set-V-org.infinispan.commons.api.functional.MetaParam.Writable...-[`set(V, MetaParam.Writable...)`]
method allows for a new value to be
associated with the cache entry for which this function is executed, and it
optionally takes zero or more metadata parameters to be stored along with
the value (see <<_meta_parameter, Metadata Parameter Handling section>> to
find out more).
* link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.WriteEntryView.html#remove--[`remove()`]
method removes the cache entry, including both value and metadata
parameters associated with this key.

=== Read-Write Map API

The final type of operations we have are read­write operations, and within
this category CAS-like (Compare­And­Swap) operations can be found.
This type of operations require previous value associated with the key
to be read and for locks to be acquired before executing the function.
The vast majority of operations within
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html[`ConcurrentMap`]
and
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java[`JCache`]
APIs fall within this category, and they can easily be implemented using the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadWriteMap.html[read-write map API]
. Moreover, with
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadWriteMap.html[read-write map API]
, you can make CAS­like comparisons not only based on value equality
but based on metadata parameter equality such as version information,
and you can send back previous value or boolean instances to signal
whether the CAS­like comparison succeeded.

Implementing a write operation that returns the previous value associated
with the cache entry is easy to achieve with the read-write map API:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;

ReadWriteMap<String, String> readWriteMap = ...

CompletableFuture<Optional<String>> readWriteFuture = readWriteMap.eval("key1", "value1",
   (v, view) -> {
      Optional<V> prev = rw.find();
      view.set(v);
      return prev;
   });
readWriteFuture.thenAccept(System.out::println);
----

link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html#replace-K-V-V-[`ConcurrentMap.replace(K, V, V)`]
is a replace function that compares the
value present in the map and if it's equals to the value passed in as
first parameter, the second value is stored, returning a boolean
indicating whether the replace was successfully completed. This operation
can easily be implemented using the read-write map API:

[source,java]
----
ReadWriteMap<String, String> readWriteMap = ...

String oldValue = "old-value";
CompletableFuture<Boolean> replaceFuture = readWriteMap.eval("key1", "value1", (v, view) -> {
   return view.find().map(prev -> {
      if (prev.equals(oldValue)) {
         rw.set(v);
         return true; // previous value present and equals to the expected one
      }
      return false; // previous value associated with key does not match
   }).orElse(false); // no value associated with this key
});
replaceFuture.thenAccept(replaced -> System.out.printf("Value was replaced? %s%n", replaced));
----

NOTE: The function in the example above captures `oldValue` which is an
external value to the function which is valid use case.

Read-write map API contains `evalMany` and `evalAll` operations which behave
similar to the write-only map offerings, except that they enable previous
value and metadata parameters to be read.

[[_read_write_entry_view]]
==== Read-Write Entry View
The function parameters for read-write maps provide the user with the
possibility to query the information associated with the key, including
value and metadata parameters, and the user can also use this
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.ReadWriteEntryView.html[read-write entry view]
to modify the data in the cache.

The operations are exposed by read-write entry views are a union of
the operations exposed by <<_read-only_entry_view, read-only entry views>>
and <<_write_only_entry_view, write-only entry views>>

[[_meta_parameter]]
=== Metadata Parameter Handling
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/MetaParam.html[Metadata parameters]
provide extra information about the cache entry, such
as version information, lifespan, last accessed/used time...etc. Some of
these can be provided by the user, e.g. version, lifespan...etc, but some
others are computed internally and can only be queried, e.g. last
accessed/used time.

The functional map API provides a flexible way to store metadata parameters
along with an cache entry. To be able to store a metadata parameter, it must
extend
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/MetaParam.Lookup.html[`MetaParam.Writable`]
interface, and implement the methods to allow the
internal logic to extra the data. Storing is done via the
`set(V, MetaParam.Writable...)` method in
<<_write_only_entry_view, write-only entry view>> or
<<_read_write_entry_view, read-write entry view>> function parameters.

Querying metadata parameters is available via the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/MetaParam.Lookup.html#findMetaParam-java.lang.Class-[`findMetaParam(Class)`]
method
available via <<_read_write_entry_view, read-write entry view>> or
<<_read_only_entry_view, read-only entry view>> or function parameters.

Here is an example showing how to store metadata parameters and how to query
them:

[source,java]
----
import java.time.Duration;
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.api.functional.MetaParam.*;

WriteOnlyMap<String, String> writeOnlyMap = ...
ReadOnlyMap<String, String> readOnlyMap = ...

CompletableFuture<Void> writeFuture = writeOnlyMap.eval("key1", "value1",
   (v, view) -> view.set(v, new MetaLifespan(Duration.ofHours(1).toMillis())));
CompletableFuture<MetaLifespan> readFuture = writeFuture.thenCompose(r ->
   readOnlyMap.eval("key1", view -> view.findMetaParam(MetaLifespan.class).get()));
readFuture.thenAccept(System.out::println);
----

If the metadata parameter is generic, for example
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/MetaParam.MetaEntryVersion.html[`MetaEntryVersion<T>`]
, retrieving the metadata parameter along with a specific type can be tricky
if using `.class` static helper in a class because it does not return a
`Class<T>` but only `Class`, and hence any generic information in the class is
lost:

[source,java]
----
ReadOnlyMap<String, String> readOnlyMap = ...

CompletableFuture<String> readFuture = readOnlyMap.eval("key1", view -> {
   // If caller depends on the typed information, this is not an ideal way to retrieve it
   // If the caller does not depend on the specific type, this works just fine.
   Optional<MetaEntryVersion> version = view.findMetaParam(MetaEntryVersion.class);
   return view.get();
});
----

When generic information is important the user can define a static helper
method that coerces the static class retrieval to the type requested,
and then use that helper method in the call to `findMetaParam`:

[source,java]
----
class MetaEntryVersion<T> implements MetaParam.Writable<EntryVersion<T>> {
   ...
   public static <T> T type() { return (T) MetaEntryVersion.class; }
   ...
}

ReadOnlyMap<String, String> readOnlyMap = ...

CompletableFuture<String> readFuture = readOnlyMap.eval("key1", view -> {
   // The caller wants guarantees that the metadata parameter for version is numeric
   // e.g. to query the actual version information
   Optional<MetaEntryVersion<Long>> version = view.findMetaParam(MetaEntryVersion.type());
   return view.get();
});
----

Finally, users are free to create new instances of metadata parameters to
suit their needs. They are stored and retrieved in the very same way as done
for the metadata parameters already provided by the functional map API.

[[_invocation_parameter]]
=== Invocation Parameter
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Param.html[Per-invocation parameters]
are applied to regular functional map API calls to
alter the behaviour of certain aspects. Adding per invocation parameters is
done using the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.html#withParams-org.infinispan.commons.api.functional.Param...-[`withParams(Param<?>...)`]
method.

link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Param.FutureMode.html[`Param.FutureMode`]
tweaks whether a method returning a
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html[`CompletableFuture`]
will span a thread to invoke the method, or instead will use the caller
thread. By default, whenever a call is made to a method returning a
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html[`CompletableFuture`]
, a separate thread will be span to execute the method asynchronously.
However, if the caller will immediately block waiting for the
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html[`CompletableFuture`]
to complete, spanning a different thread is wasteful, and hence
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Param.FutureMode.html#COMPLETED[`Param.FutureMode.COMPLETED`]
can be passed as per-invocation parameter to avoid creating that extra thread. Example:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.api.functional.Param.*;

ReadOnlyMap<String, String> readOnlyMap = ...
ReadOnlyMap<String, String> readOnlyMapCompleted = readOnlyMap.withParams(FutureMode.COMPLETED);
Optional<String> readFuture = readOnlyMapCompleted.eval("key1", ReadEntryView::find).get();
----

Param.PersistenceMode controls whether a write operation will be propagated
to a persistence store. The default behaviour is for all write-operations
to be propagated to the persistence store if the cache is configured with
a persistence store. By passing PersistenceMode.SKIP as parameter,
the write operation skips the persistence store and its effects are only
seen in the in-memory contents of the cache. PersistenceMode.SKIP can
be used to implement an
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/Cache.html#evict-K-[`Cache.evict()`]
method which removes data from memory but leaves the persistence store
untouched:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.api.functional.Param.*;

WriteOnlyMap<String, String> writeOnlyMap = ...
WriteOnlyMap<String, String> skiPersistMap = writeOnlyMap.withParams(PersistenceMode.SKIP);
CompletableFuture<Void> removeFuture = skiPersistMap.eval("key1", WriteEntryView::remove);
----

Note that there's no need for another PersistenceMode option to skip
reading from the persistence store, because a write operation can skip
reading previous value from the store by calling a write-only operation
via the WriteOnlyMap.

Finally, new Param implementations are normally provided by the functional
map API since they tweak how the internal logic works. So, for the most part
of users, they should limit themselves to using the Param instances exposed
by the API. The exception to this rule would be advanced users who decide
to add new interceptors to the internal stack. These users have the ability
to query these parameters within the interceptors.

[[_functional_listeners]]
=== Functional Listeners
The functional map offers a listener API, where clients can register for and
get notified when events take place. These notifications are post-event, so
that means the events are received after the event has happened.

The listeners that can be registered are split into two categories:
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.WriteListeners.html[write listeners]
and
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.html[read-write listeners].

==== Write Listeners
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.WriteListeners.html[Write listeners]
enable user to register listeners for any cache entry write events
that happen in either a read-write or write-only functional map.

Listeners for write events cannot distinguish between cache entry
created and cache entry modify/update events because they don't have
access to the previous value. All they know is that a new non-null
entry has been written.

However, write event listeners can distinguish between entry removals
and cache entry create/modify-update events because they can query
what the new entry's value via
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/EntryView.ReadEntryView.html#find--[`ReadEntryView.find()`]
method.

Adding a write listener is done via the WriteListeners interface
which is accessible via both
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadWriteMap.html#listeners--[`ReadWriteMap.listeners()`]
and
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.WriteOnlyMap.html#listeners--[`WriteOnlyMap.listeners()`]
 method.

A write listener implementation can be defined either passing a function
to
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.WriteListeners.html#onWrite-java.util.function.Consumer-[`onWrite(Consumer<ReadEntryView<K, V>>)`]
method, or passing a
WriteListener implementation to
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.WriteListeners.html#add-org.infinispan.commons.api.functional.Listeners.WriteListeners.WriteListener-[`add(WriteListener<K, V>)`]
method.
Either way, all these methods return an
link:https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html[AutoCloseable]
instance that can be used to de-register the function listener:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.api.functional.Listeners.WriteListeners.WriteListener;

WriteOnlyMap<String, String> woMap = ...

AutoCloseable writeFunctionCloseHandler = woMap.listeners().onWrite(written -> {
   // `written` is a ReadEntryView of the written entry
   System.out.printf("Written: %s%n", written.get());
});
AutoCloseable writeCloseHanlder = woMap.listeners().add(new WriteListener<String, String>() {
   @Override
   public void onWrite(ReadEntryView<K, V> written) {
      System.out.printf("Written: %s%n", written.get());
   }
});

// Either wrap handler in a try section to have it auto close...
try(writeFunctionCloseHandler) {
   // Write entries using read-write or write-only functional map API
   ...
}
// Or close manually
writeCloseHanlder.close();
----

==== Read-Write Listeners
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.html[Read-write listeners]
enable users to register listeners for cache entry created, modified
and removed events, and also register listeners for any cache entry
write events.

Entry created, modified and removed events can only be fired when these
originate on a read-write functional map, since this is the only one
that guarantees that the previous value has been read, and hence the
differentiation between create, modified and removed can be fully
guaranteed.

Adding a read-write listener is done via the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.html[`ReadWriteListeners`]
interface which is accessible via
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/FunctionalMap.ReadWriteMap.html#listeners--[`ReadWriteMap.listeners()`]
method.

If interested in only one of the event types, the simplest way to add a
listener is to pass a function to either
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.ReadWriteListener.html#onCreate-org.infinispan.commons.api.functional.EntryView.ReadEntryView-[`onCreate`]
,
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.ReadWriteListener.html#onModify-org.infinispan.commons.api.functional.EntryView.ReadEntryView-org.infinispan.commons.api.functional.EntryView.ReadEntryView-[`onModify`]
or
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.ReadWriteListener.html#onRemove-org.infinispan.commons.api.functional.EntryView.ReadEntryView-[`onRemove`]
methods. All these methods return an AutoCloseable instance that can be
used to de-register the function listener:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;

ReadWriteMap<String, String> rwMap = ...
AutoCloseable createClose = rwMap.listeners().onCreate(created -> {
   // `created` is a ReadEntryView of the created entry
   System.out.printf("Created: %s%n", created.get());
});
AutoCloseable modifyClose = rwMap.listeners().onModify((before, after) -> {
   // `before` is a ReadEntryView of the entry before update
   // `after` is a ReadEntryView of the entry after update
   System.out.printf("Before: %s%n", before.get());
   System.out.printf("After: %s%n", after.get());
});
AutoCloseable removeClose = rwMap.listeners().onRemove(removed -> {
   // `removed` is a ReadEntryView of the removed entry
   System.out.printf("Removed: %s%n", removed.get());
});
AutoCloseable writeClose = woMap.listeners().onWrite(written -> {
   // `written` is a ReadEntryView of the written entry
   System.out.printf("Written: %s%n", written.get());
});
...
// Either wrap handler in a try section to have it auto close...
try(createClose) {
   // Create entries using read-write functional map API
   ...
}
// Or close manually
modifyClose.close();
----

If listening for two or more event types, it's better to pass in an
implementation of
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.WriteListeners.WriteListener.html[`ReadWriteListener`]
interface via the
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/api/functional/Listeners.ReadWriteListeners.html#add-org.infinispan.commons.api.functional.Listeners.ReadWriteListeners.ReadWriteListener-[`ReadWriteListeners.add()`]
method. `ReadWriteListener` offers the same `onCreate`/`onModify`/`onRemove`
callbacks with default method implementations that are empty:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.api.functional.Listeners.ReadWriteListeners.ReadWriteListener;

ReadWriteMap<String, String> rwMap = ...
AutoCloseable readWriteClose = rwMap.listeners.add(new ReadWriteListener<String, String>() {
   @Override
   public void onCreate(ReadEntryView<String, String> created) {
      System.out.printf("Created: %s%n", created.get());
   }

   @Override
   public void onModify(ReadEntryView<String, String> before, ReadEntryView<String, String> after) {
      System.out.printf("Before: %s%n", before.get());
      System.out.printf("After: %s%n", after.get());
   }

   @Override
   public void onRemove(ReadEntryView<String, String> removed) {
      System.out.printf("Removed: %s%n", removed.get());
   }
);
AutoCloseable writeClose = rwMap.listeners.add(new WriteListener<String, String>() {
   @Override
   public void onWrite(ReadEntryView<K, V> written) {
      System.out.printf("Written: %s%n", written.get());
   }
);

// Either wrap handler in a try section to have it auto close...
try(readWriteClose) {
   // Create/update/remove entries using read-write functional map API
   ...
}
// Or close manually
writeClose.close();
----

=== Marshalling of Functions
Running functional map in a cluster of nodes involves marshalling and
replication of the operation parameters under certain circumstances.

To be more precise, when write operations are executed in a cluster,
regardless of read-write or write-only operations, all the parameters
to the method and the functions are replicated to other nodes.

There are multiple ways in which a function can be marshalled. The simplest
way, which is also the most costly option in terms of payload size, is
to mark the function as
link:http://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html[`Serializable`]
:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;

WriteOnlyMap<String, String> writeOnlyMap = ...

// Force a function to be Serializable
Consumer<WriteEntryView<String>> function =
   (Consumer<WriteEntryView<String>> & Serializable) wv -> wv.set("one");

CompletableFuture<Void> writeFuture = writeOnlyMap.eval("key1", function);
----

A more economical way to marshall a function is to provide an Infinispan
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/marshall/Externalizer.html[`Externalizer`]
for it:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.commons.marshall.Externalizer;
import org.infinispan.commons.marshall.SerializeFunctionWith;

WriteOnlyMap<String, String> writeOnlyMap = ...

// Force a function to be Serializable
Consumer<WriteEntryView<String>> function = new SetStringConstant<>();
CompletableFuture<Void> writeFuture = writeOnlyMap.eval("key1", function);

@SerializeFunctionWith(value = SetStringConstant.Externalizer0.class)
class SetStringConstant implements Consumer<WriteEntryView<String>> {
   @Override
   public void accept(WriteEntryView<String> view) {
      view.set("value1");
   }

   public static final class Externalizer0 implements Externalizer<Object> {
      public void writeObject(ObjectOutput oo, Object o) {
         // No-op
      }
      public Object readObject(ObjectInput input) {
         return new SetStringConstant<>();
      }
   }
}
----

To help users take advantage of the tiny payloads generated by
`Externalizer`-based functions, the functional API comes with a helper
class called
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/marshall/MarshallableFunctions.html[`org.infinispan.marshall.core.MarshallableFunctions`]
which provides marshallable functions for some of the most commonly user
functions.

In fact, all the functions required to implement
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html[`ConcurrentMap`]
and
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java[`JCache`]
using the functional map API have been defined in
link:https://docs.jboss.org/infinispan/8.0/apidocs/org/infinispan/commons/marshall/MarshallableFunctions.html[`MarshallableFunctions`].
For example, here is an implementation of JCache's
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java#L283[`boolean putIfAbsent(K, V)`]
using functional map API which can be run in a cluster:

[source,java]
----
import org.infinispan.commons.api.functional.EntryView.*;
import org.infinispan.commons.api.functional.FunctionalMap.*;
import org.infinispan.marshall.core.MarshallableFunctions;

ReadWriteMap<String, String> readWriteMap = ...

CompletableFuture<Boolean> future = readWriteMap.eval("key1,
   MarshallableFunctions.setValueIfAbsentReturnBoolean());
future.thenAccept(stored -> System.out.printf("Value was put? %s%n", stored));
----

=== Use cases for Functional API

This new API is meant to complement existing Key/Value Infinispan API
offerings, so you'll still be able to use
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html[`ConcurrentMap`]
or
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java[`JCache`]
standard APIs if that's what suits your use case best.

The target audience for this new API is either:

* Distributed or persistent caching/in­memory­data­grid users that want
to benefit from CompletableFuture and/or Traversable for async/lazy data
grid or caching data manipulation. The clear advantage here is that threads
do not need to be idle waiting for remote operations to complete, but
instead these can be notified when remote operations complete and then
chain them with other subsequent operations.

* Users wanting to go beyond the standard operations exposed by
link:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html[`ConcurrentMap`]
and
link:https://github.com/jsr107/jsr107spec/blob/v1.0.0/src/main/java/javax/cache/Cache.java[`JCache`]
, for example, if you want to do a replace
operation using metadata parameter equality instead of value equality, or
if you want to retrieve metadata information from values...etc.
