
com.hazelcast.spi.impl.DelegatingCompletableFuture Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.spi.impl;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.internal.serialization.SerializationService;
import com.hazelcast.nio.serialization.HazelcastSerializationException;
import javax.annotation.Nonnull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import static com.hazelcast.internal.util.ConcurrencyUtil.CALLER_RUNS;
import static com.hazelcast.internal.util.ExceptionUtil.sneakyThrow;
import static java.util.Objects.requireNonNull;
/**
* A {@link InternalCompletableFuture} implementation that delegates the real logic to an underlying
* {@link InternalCompletableFuture} and decorates it with additional behavior:
*
* - change the returned value by setting the result
* - caching the deserialized content so that a deserialization only happens once. This should be used with
* care since this could lead to unexpected sharing of object instances if the same future is shared between
* threads.
*
*
*
* Even though the wrapped future may be completed normally, it is possible that
* a {@link HazelcastSerializationException} thrown when deserializing the value
* will result in this future being completed exceptionally. A deserialization
* failure makes this future being considered to complete exceptionally,
* therefore futures from dependent stages will be completed with a
* HazelcastSerializationException (unless the dependent stage transforms
* the outcome).
*
* @param
*/
@SuppressWarnings("checkstyle:methodcount")
public class DelegatingCompletableFuture extends InternalCompletableFuture {
protected static final Object VOID = new Object() {
@Override
public String toString() {
return "void";
}
};
private static final AtomicReferenceFieldUpdater DESERIALIZED_VALUE
= AtomicReferenceFieldUpdater.newUpdater(DelegatingCompletableFuture.class,
Object.class, "deserializedValue");
protected final CompletableFuture future;
protected final InternalSerializationService serializationService;
protected final Object result;
protected volatile Object deserializedValue = VOID;
public DelegatingCompletableFuture(@Nonnull SerializationService serializationService,
@Nonnull CompletableFuture future) {
this(serializationService, future, null);
}
public DelegatingCompletableFuture(@Nonnull SerializationService serializationService,
@Nonnull CompletableFuture future,
V result) {
this(serializationService, future, result, true);
}
protected DelegatingCompletableFuture(@Nonnull SerializationService serializationService,
@Nonnull CompletableFuture future,
V result,
boolean listenFutureCompletion) {
this.future = future;
this.serializationService = (InternalSerializationService) serializationService;
this.result = result;
if (listenFutureCompletion) {
this.future.whenCompleteAsync((v, t) -> completeSuper(v, (Throwable) t), CALLER_RUNS);
}
}
@Override
public V get() throws InterruptedException, ExecutionException {
try {
return resolve(future.get());
} catch (HazelcastSerializationException e) {
throw new ExecutionException(e);
}
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
try {
return resolve(future.get(timeout, unit));
} catch (HazelcastSerializationException e) {
throw new ExecutionException(e);
}
}
@Override
public V getNow(V valueIfAbsent) {
// if there is an explicit value set, we use that
if (result != null) {
return (V) result;
}
// if there already is a deserialized value set, use it.
if (deserializedValue != VOID) {
return (V) deserializedValue;
}
// otherwise, do not cache the value returned from future.getNow
// because it might be the default valueIfAbsent
Object value = future.getNow(valueIfAbsent);
try {
return (value instanceof Data)
? serializationService.toObject(value) : (V) value;
} catch (HazelcastSerializationException e) {
throw new CompletionException(e);
}
}
@Override
public V join() {
try {
return resolve(future.join());
} catch (HazelcastSerializationException e) {
throw new CompletionException(e);
}
}
@Override
public V joinInternal() {
if (future instanceof InternalCompletableFuture completableFuture) {
return resolve(completableFuture.joinInternal());
} else {
try {
return resolve(future.join());
} catch (CompletionException e) {
Throwable cause = e.getCause();
throw sneakyThrow(AbstractInvocationFuture.wrapOrPeel(cause));
}
}
}
// public for testing
public Future getDelegate() {
return future;
}
// Overriding this method means you also have to override getNow
protected V resolve(Object object) {
// if there is an explicit value set, we use that
if (result != null) {
return (V) result;
}
// if there already is a deserialized value set, use it.
if (deserializedValue != VOID) {
return (V) deserializedValue;
}
if (object instanceof Data data) {
// we need to deserialize.
object = serializationService.toObject(data);
serializationService.disposeData(data);
object = cacheDeserializedValue(object);
}
return (V) object;
}
protected Object cacheDeserializedValue(Object object) {
for (; ; ) {
Object current = deserializedValue;
if (current != VOID) {
object = current;
break;
} else if (DESERIALIZED_VALUE.compareAndSet(this, VOID, object)) {
break;
}
}
return object;
}
@Override
public boolean isDone() {
return future.isDone();
}
@Override
public boolean complete(V value) {
boolean triggered = future.complete(value);
if (triggered) {
super.complete(value);
}
return triggered;
}
@Override
public boolean completeExceptionally(Throwable ex) {
boolean triggered = future.completeExceptionally(ex);
if (triggered) {
super.completeExceptionally(ex);
}
return triggered;
}
@Override
public CompletableFuture thenApply(Function super V, ? extends U> fn) {
return future.thenApply(new DeserializingFunction<>(serializationService, fn));
}
@Override
public CompletableFuture thenApplyAsync(Function super V, ? extends U> fn) {
return future.thenApplyAsync(new DeserializingFunction<>(serializationService, fn));
}
@Override
public CompletableFuture thenApplyAsync(Function super V, ? extends U> fn, Executor executor) {
return future.thenApplyAsync(new DeserializingFunction<>(serializationService, fn), executor);
}
@Override
public CompletableFuture thenAccept(Consumer super V> action) {
return future.thenAccept(new DeserializingConsumer<>(serializationService, action));
}
@Override
public CompletableFuture thenAcceptAsync(Consumer super V> action) {
return future.thenAcceptAsync(new DeserializingConsumer<>(serializationService, action));
}
@Override
public CompletableFuture thenAcceptAsync(Consumer super V> action, Executor executor) {
return future.thenAcceptAsync(new DeserializingConsumer<>(serializationService, action), executor);
}
@Override
public CompletableFuture thenRun(Runnable action) {
return future.thenRun(new DeserializingRunnable(serializationService, action));
}
@Override
public CompletableFuture thenRunAsync(Runnable action) {
return future.thenRunAsync(new DeserializingRunnable(serializationService, action));
}
@Override
public CompletableFuture thenRunAsync(Runnable action, Executor executor) {
return future.thenRunAsync(new DeserializingRunnable(serializationService, action), executor);
}
@Override
public CompletableFuture thenCombine(CompletionStage extends U> other,
BiFunction super V, ? super U, ? extends V1> fn) {
return future.thenCombine(other, new DeserializingBiFunction<>(serializationService, fn));
}
@Override
public CompletableFuture thenCombineAsync(CompletionStage extends U> other,
BiFunction super V, ? super U, ? extends V1> fn) {
return future.thenCombineAsync(other, new DeserializingBiFunction<>(serializationService, fn));
}
@Override
public CompletableFuture thenCombineAsync(CompletionStage extends U> other,
BiFunction super V, ? super U, ? extends V1> fn, Executor executor) {
return future.thenCombineAsync(other, new DeserializingBiFunction<>(serializationService, fn), executor);
}
@Override
public CompletableFuture thenAcceptBoth(CompletionStage extends U> other,
BiConsumer super V, ? super U> action) {
return future.thenAcceptBoth(other, new DeserializingBiConsumer<>(serializationService, action));
}
@Override
public CompletableFuture thenAcceptBothAsync(CompletionStage extends U> other,
BiConsumer super V, ? super U> action) {
return future.thenAcceptBothAsync(other, new DeserializingBiConsumer<>(serializationService, action));
}
@Override
public CompletableFuture thenAcceptBothAsync(CompletionStage extends U> other,
BiConsumer super V, ? super U> action, Executor executor) {
return future.thenAcceptBothAsync(other, new DeserializingBiConsumer<>(serializationService, action), executor);
}
@Override
public CompletableFuture runAfterBoth(CompletionStage> other, Runnable action) {
return future.runAfterBoth(other, new DeserializingRunnable(serializationService, action));
}
@Override
public CompletableFuture runAfterBothAsync(CompletionStage> other, Runnable action) {
return future.runAfterBothAsync(other, new DeserializingRunnable(serializationService, action));
}
@Override
public CompletableFuture runAfterBothAsync(CompletionStage> other, Runnable action, Executor executor) {
return future.runAfterBothAsync(other, new DeserializingRunnable(serializationService, action), executor);
}
@Override
public CompletableFuture applyToEither(CompletionStage extends V> other, Function super V, U> fn) {
return future.applyToEither(other, new DeserializingFunction<>(serializationService, fn));
}
@Override
public CompletableFuture applyToEitherAsync(CompletionStage extends V> other, Function super V, U> fn) {
return future.applyToEitherAsync(other, new DeserializingFunction<>(serializationService, fn));
}
@Override
public CompletableFuture applyToEitherAsync(CompletionStage extends V> other, Function super V, U> fn,
Executor executor) {
return future.applyToEitherAsync(other, new DeserializingFunction<>(serializationService, fn), executor);
}
@Override
public CompletableFuture acceptEither(CompletionStage extends V> other, Consumer super V> action) {
return future.acceptEither(other, new DeserializingConsumer<>(serializationService, action));
}
@Override
public CompletableFuture acceptEitherAsync(CompletionStage extends V> other, Consumer super V> action) {
return future.acceptEitherAsync(other, new DeserializingConsumer<>(serializationService, action));
}
@Override
public CompletableFuture acceptEitherAsync(CompletionStage extends V> other, Consumer super V> action,
Executor executor) {
return future.acceptEitherAsync(other, new DeserializingConsumer<>(serializationService, action), executor);
}
@Override
public CompletableFuture runAfterEither(CompletionStage> other, Runnable action) {
return future.runAfterEither(other, new DeserializingRunnable(serializationService, action));
}
@Override
public CompletableFuture runAfterEitherAsync(CompletionStage> other, Runnable action) {
return future.runAfterEitherAsync(other, new DeserializingRunnable(serializationService, action));
}
@Override
public CompletableFuture runAfterEitherAsync(CompletionStage> other, Runnable action, Executor executor) {
return future.runAfterEitherAsync(other, new DeserializingRunnable(serializationService, action), executor);
}
@Override
public CompletableFuture thenCompose(Function super V, ? extends CompletionStage> fn) {
return future.thenCompose(new DeserializingFunction<>(serializationService, fn));
}
@Override
public CompletableFuture thenComposeAsync(Function super V, ? extends CompletionStage> fn) {
return future.thenComposeAsync(new DeserializingFunction<>(serializationService, fn));
}
@Override
public CompletableFuture thenComposeAsync(Function super V, ? extends CompletionStage> fn, Executor executor) {
return future.thenComposeAsync(new DeserializingFunction<>(serializationService, fn), executor);
}
@Override
public CompletableFuture whenComplete(BiConsumer super V, ? super Throwable> action) {
return new DelegatingCompletableFuture<>(serializationService,
future.whenComplete(new WhenCompleteBiConsumer(serializationService, action)));
}
@Override
public CompletableFuture whenCompleteAsync(BiConsumer super V, ? super Throwable> action) {
return new DelegatingCompletableFuture<>(serializationService,
future.whenCompleteAsync(new WhenCompleteBiConsumer(serializationService, action)));
}
@Override
public CompletableFuture whenCompleteAsync(BiConsumer super V, ? super Throwable> action, Executor executor) {
return new DelegatingCompletableFuture<>(serializationService,
future.whenCompleteAsync(new WhenCompleteBiConsumer(serializationService, action), executor));
}
@Override
public CompletableFuture handle(BiFunction super V, Throwable, ? extends U> fn) {
return future.handle(new HandleBiFunction(serializationService, fn));
}
@Override
public CompletableFuture handleAsync(BiFunction super V, Throwable, ? extends U> fn) {
return future.handleAsync(new HandleBiFunction<>(serializationService, fn));
}
@Override
public CompletableFuture handleAsync(BiFunction super V, Throwable, ? extends U> fn, Executor executor) {
return future.handleAsync(new HandleBiFunction<>(serializationService, fn), executor);
}
@Override
public CompletableFuture toCompletableFuture() {
return this;
}
@Override
public CompletableFuture exceptionally(Function fn) {
return future.handle(new ExceptionallyBiFunction(serializationService, fn));
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return future.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return future.isCancelled();
}
@Override
public boolean isCompletedExceptionally() {
// if super is completed, then value deserialization has already happened,
// so we know if this future is completed exceptionally
if (super.isDone()) {
return super.isCompletedExceptionally();
}
// otherwise, check the delegate future: if that one is done, try
// resolve the completion value
if (future.isDone()) {
try {
resolve(future.join());
return false;
} catch (Throwable t) {
return true;
}
} else {
return false;
}
}
@Override
public void obtrudeValue(V value) {
future.obtrudeValue(value);
}
@Override
public void obtrudeException(Throwable ex) {
future.obtrudeException(ex);
}
@Override
public int getNumberOfDependents() {
return future.getNumberOfDependents();
}
@Override
public String toString() {
return future.toString();
}
// used for testing
public V getDeserializedValue() {
return (V) deserializedValue;
}
protected void completeSuper(Object value, Throwable t) {
if (t != null) {
super.completeExceptionally(t);
} else {
try {
V resolved = resolve(value);
super.complete(resolved);
} catch (HazelcastSerializationException e) {
super.completeExceptionally(e);
}
}
}
static class DeserializingFunction implements Function {
private final SerializationService serializationService;
private final Function delegate;
DeserializingFunction(SerializationService serializationService, Function delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public R apply(E e) {
return delegate.apply(serializationService.toObject(e));
}
}
class DeserializingRunnable implements Runnable {
private final SerializationService serializationService;
private final Runnable delegate;
DeserializingRunnable(SerializationService serializationService, Runnable delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public void run() {
// deserialize to ensure no HazelcastSerializationException occurs
serializationService.toObject(future.join());
delegate.run();
}
}
static class DeserializingConsumer implements Consumer {
private final SerializationService serializationService;
private final Consumer delegate;
DeserializingConsumer(SerializationService serializationService, Consumer delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public void accept(E e) {
delegate.accept(serializationService.toObject(e));
}
}
static class DeserializingBiFunction implements BiFunction {
private final SerializationService serializationService;
private final BiFunction delegate;
DeserializingBiFunction(SerializationService serializationService, BiFunction delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public R apply(T t, U u) {
T v1 = serializationService.toObject(t);
U v2 = serializationService.toObject(u);
return delegate.apply(v1, v2);
}
}
static class HandleBiFunction implements BiFunction {
private final SerializationService serializationService;
private final BiFunction delegate;
HandleBiFunction(SerializationService serializationService, BiFunction delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public R apply(T t, U u) {
T deserialized = null;
try {
deserialized = serializationService.toObject(t);
} catch (HazelcastSerializationException exc) {
u = (U) exc;
}
return delegate.apply(deserialized, u);
}
}
static class DeserializingBiConsumer implements BiConsumer {
private final SerializationService serializationService;
private final BiConsumer delegate;
DeserializingBiConsumer(SerializationService serializationService, BiConsumer delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public void accept(T t, U u) {
T v1 = serializationService.toObject(t);
U v2 = serializationService.toObject(u);
delegate.accept(v1, v2);
}
}
static class WhenCompleteBiConsumer implements BiConsumer {
private final SerializationService serializationService;
private final BiConsumer delegate;
WhenCompleteBiConsumer(SerializationService serializationService, BiConsumer delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public void accept(E v, T t) {
E deserialized = null;
try {
deserialized = serializationService.toObject(v);
} catch (HazelcastSerializationException exc) {
t = (T) exc;
}
delegate.accept(deserialized, t);
}
}
static class ExceptionallyBiFunction implements BiFunction {
private final SerializationService serializationService;
private final Function delegate;
ExceptionallyBiFunction(SerializationService serializationService, Function delegate) {
requireNonNull(delegate);
this.serializationService = serializationService;
this.delegate = delegate;
}
@Override
public R apply(T t, U u) {
if (u != null) {
return delegate.apply(u);
} else {
R deserialized;
try {
deserialized = serializationService.toObject(t);
return deserialized;
} catch (HazelcastSerializationException exc) {
u = (U) exc;
return delegate.apply(u);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy