All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smallrye.mutiny.Context Maven / Gradle / Ivy

There is a newer version: 2.8.0
Show newest version
package io.smallrye.mutiny;

import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull;
import static java.util.Objects.requireNonNull;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Flow;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
 * A context allows sharing key / value entries along with a subscriber in a Mutiny pipeline, so all operators can
 * share implicit data for a given subscription.
 * 

* A context is provided by a {@link io.smallrye.mutiny.subscription.UniSubscriber} or {@link Flow.Subscriber} * that implements {@link io.smallrye.mutiny.subscription.ContextSupport}. *

* Context keys are represented as {@link String} while values can be from heterogeneous types. *

* {@link Context} instances are thread-safe. * Internal storage is not allocated until the first entry is being added. *

* Contexts shall be primarily used to share transient data used for networked I/O processing such as correlation * identifiers, tokens, etc. * They should not be used as general-purpose data structures that are frequently updated and that hold large amounts of * data. * * @see Uni#withContext(BiFunction) * @see Uni#attachContext() * @see io.smallrye.mutiny.groups.UniSubscribe * @see Multi#withContext(BiFunction) * @see Multi#attachContext() * @see io.smallrye.mutiny.groups.MultiSubscribe */ public final class Context { /** * Creates a new empty context. * * @return the context */ public static Context empty() { return new Context(); } /** * Creates a context from key / value pairs. * * @param entries a sequence of key / value pairs, as in {@code key1, value1, key2, value2} * @return the new context * @throws IllegalArgumentException when {@code entries} is not balanced * @throws NullPointerException when {@code entries} is {@code null} */ public static Context of(Object... entries) { requireNonNull(entries, "The entries array cannot be null"); if (entries.length % 2 != 0) { throw new IllegalArgumentException("Arguments must be balanced to form (key, value) pairs"); } HashMap map = new HashMap<>(); for (int i = 0; i < entries.length; i = i + 2) { String key = nonNull(entries[i], "key").toString(); Object value = nonNull(entries[i + 1], "value"); map.put(key, value); } return new Context(map); } /** * Creates a context by copying the entries from a {@link Map}. * * @param entries the map, cannot be {@code null} * @return the new context * @throws NullPointerException when {@code entries} is null */ public static Context from(Map entries) { return new Context(requireNonNull(entries, "The entries map cannot be null")); } private volatile ConcurrentHashMap entries; private Context() { this.entries = null; } private Context(Map initialEntries) { this.entries = new ConcurrentHashMap<>(initialEntries); } /** * Checks whether the context has an entry for a given key. * * @param key the key * @return {@code true} when there is an entry for {@code key}, {@code false} otherwise */ public boolean contains(String key) { if (entries == null) { return false; } else { return entries.containsKey(key); } } /** * Get a value for a key. * * @param key the key * @param the value type * @return the value * @throws NoSuchElementException when there is no entry for {@code key} */ @SuppressWarnings("unchecked") public T get(String key) throws NoSuchElementException { if (entries == null) { throw new NoSuchElementException("The context is empty"); } T value = (T) entries.get(key); if (value == null) { throw new NoSuchElementException("The context does not have a value for key " + key); } return value; } /** * Get a value for a key, or provide an alternative value when not present. * * @param key the key * @param alternativeSupplier the alternative value supplier when there is no entry for {@code key} * @param the value type * @return the value */ @SuppressWarnings("unchecked") public T getOrElse(String key, Supplier alternativeSupplier) { if (entries != null) { T value = (T) entries.get(key); if (value != null) { return value; } } return alternativeSupplier.get(); } /** * Stores a value for a given key. * * @param key the key * @param value the value, cannot be {@code null} * @return this context */ public Context put(String key, Object value) { if (entries == null) { synchronized (this) { if (entries == null) { this.entries = new ConcurrentHashMap<>(8); } } } entries.put(key, value); return this; } /** * Delete an entry for a given key, if present. * * @param key the key * @return this context */ public Context delete(String key) { if (entries != null) { entries.remove(key); } return this; } /** * Test whether the context is empty. * * @return {@code true} if the context is empty, {@code false} otherwise */ public boolean isEmpty() { return (this.entries == null) || (entries.isEmpty()); } /** * Gives the set of keys present in the context at the time the method is being called. *

* Note that each call to this method produces a copy. * * @return the set of keys */ public Set keys() { if (this.entries == null) { return Collections.emptySet(); } HashSet set = new HashSet<>(); Enumeration enumeration = entries.keys(); while (enumeration.hasMoreElements()) { set.add(enumeration.nextElement()); } return set; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } Context context = (Context) other; return Objects.equals(entries, context.entries); } @Override public int hashCode() { return Objects.hash(entries); } @Override public String toString() { return "Context{" + "entries=" + entries + '}'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy