package org.eclipse.microprofile.reactive.messaging;
import java.util.*;
import io.smallrye.common.annotation.Experimental;
/**
* Message metadata containers.
*
* This class stores message metadata that can be related to the transport layer or to the business / application.
*
* Instances of this class are immutable . Modification operation returned new instances.
* Contained instances are not constrained, but should be immutable. Only one instance of each class can be stored,
* as the class is used to retrieve the metadata.
*
* You can create new instances using the {@link #of(Object...)} and {@link #from(Iterable) }methods.
*
* IMPORTANT: Experimental.
*/
@Experimental("metadata propagation is a SmallRye-specific feature")
public class Metadata implements Iterable {
private final Map, Object> backend;
private static final Metadata EMPTY = new Metadata(Collections.emptyMap());
/**
* {@link Metadata} instances must be created using the static factory methods.
*
* @param backend the backend, must not be {@code null}, must be immutable.
*/
private Metadata(Map, Object> backend) {
this.backend = Collections.unmodifiableMap(backend);
}
/**
* Returns an empty set of metadata.
*
* @return the empty instance
*/
public static Metadata empty() {
return EMPTY;
}
/**
* Returns an instance of {@link Metadata} containing a single value.
*
* @param metadata the metadata to be stored, must not be {@code null}. Should be immutable.
* @return a new {@link Metadata} instance
*/
static Metadata of(Object metadata) {
if (metadata == null) {
throw new IllegalArgumentException("`metadata` must not be `null`");
}
return new Metadata(Collections.singletonMap(metadata.getClass(), metadata));
}
/**
* Returns an instance of {@link Metadata} containing multiple values.
*
* @param metadata the metadata, must not be {@code null}, must not contain {@code null},
* must not contain multiple objects of the same class
* @return the new metadata
*/
public static Metadata of(Object... metadata) {
if (metadata == null) {
throw new IllegalArgumentException("`metadata` must not be `null`");
}
Map, Object> map = createBackingMap(Arrays.asList(metadata));
return new Metadata(map);
}
/**
* Returns an instance of {@link Metadata} containing multiple values.
*
* @param metadata the metadata, must not be {@code null}, must not contain {@code null},
* must not contain multiple objects of the same class
* @return the new metadata
*/
public static Metadata from(Iterable metadata) {
if (metadata == null) {
throw new IllegalArgumentException("`metadata` must not be `null`");
}
if (metadata instanceof Metadata) {
return (Metadata) metadata;
}
Map, Object> map = createBackingMap(metadata);
if (map.isEmpty()) {
return Metadata.empty();
}
return new Metadata(map);
}
private static Map, Object> createBackingMap(Iterable metadata) {
Map, Object> map = new HashMap<>();
for (Object item : metadata) {
if (item == null) {
throw new IllegalArgumentException("One of the metadata items is `null`");
}
// Ensure that the class is not used.
if (map.containsKey(item.getClass())) {
throw new IllegalArgumentException("Duplicate metadata detected: " + item.getClass().getName());
}
map.put(item.getClass(), item);
}
return map;
}
/**
* Creates a new instance of {@link Metadata} with the current entries, plus {@code item}.
* If the current set of metadata contains already an instance of the class of {@code item}, the value is replaced
* in the returned {@link Metadata}.
*
* @param item the metadata to be added, must not be {@code null}.
* @return the new instance of {@link Metadata}
*/
public Metadata with(Object item) {
if (item == null) {
throw new IllegalArgumentException("`item` must not be `null`");
}
Map, Object> copy = new HashMap<>(backend);
copy.put(item.getClass(), item);
return new Metadata(copy);
}
/**
* Creates a new instance of {@link Metadata} with the current entries, minus the entry associated with the given class.
* If there is no instance of the class in the current set of metadata, the same entries are composing returned instance
* of metadata.
*
* @param clazz instance from this class are removed from the metadata.
* @return the new instance of {@link Metadata}
*/
public Metadata without(Class> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("`clazz` must not be `null`");
}
Map, Object> copy = new HashMap<>(backend);
copy.remove(clazz);
return new Metadata(copy);
}
/**
* Copies the current {@link Metadata} instance.
*
* @return the new instance.
*/
public Metadata copy() {
Map, Object> copy = new HashMap<>(backend);
return new Metadata(copy);
}
/**
* Retrieves the metadata associated with given class.
*
* @param clazz the class of the metadata to retrieve, must not be {@code null}
* @return an {@link Optional} containing the associated metadata, empty if none.
*/
public Optional get(Class clazz) {
if (clazz == null) {
throw new IllegalArgumentException("`clazz` must not be `null`");
}
return Optional.ofNullable(clazz.cast(backend.get(clazz)));
}
/**
* @return an iterator to traverse the set of metadata. This method will never return {@code null}.
*/
@Override
public Iterator iterator() {
return backend.values().iterator();
}
}