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

com.lambdaworks.redis.resource.Futures Maven / Gradle / Ivy

Go to download

Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.

The newest version!
/*
 * Copyright 2011-2016 the original author or authors.
 *
 * 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.lambdaworks.redis.resource;

import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import com.lambdaworks.redis.internal.LettuceAssert;
import io.netty.util.concurrent.*;

/**
 * Utility class to support netty's future handling.
 * 
 * @author Mark Paluch
 * @since 3.4
 */
class Futures {

    /**
     * Create a promise that emits a {@code Boolean} value on completion of the {@code future}
     * 
     * @param future the future.
     * @return Promise emitting a {@code Boolean} value. {@literal true} if the {@code future} completed successfully, otherwise
     *         the cause wil be transported.
     */
    static Promise toBooleanPromise(Future future) {
        final DefaultPromise result = new DefaultPromise(GlobalEventExecutor.INSTANCE);

        future.addListener(new GenericFutureListener>() {
            @Override
            public void operationComplete(Future future) throws Exception {

                if (future.isSuccess()) {
                    result.setSuccess(true);
                } else {
                    result.setFailure(future.cause());
                }
            }
        });
        return result;
    }

    /**
     * Promise aggregator that aggregates multiple promises into one {@link Promise}. The aggregator workflow is:
     * 
    *
  1. Create a new instance of {@link com.lambdaworks.redis.resource.Futures.PromiseAggregator}
  2. *
  3. Call {@link #expectMore(int)} until the number of expected futures is reached
  4. *
  5. Arm the aggregator using {@link #arm()}
  6. *
  7. Add the number of futures using {@link #add(Promise[])} until the expectation is met. The added futures can be either * done or in progress.
  8. *
  9. The {@code aggregatePromise} is released/finished as soon as the last future/promise completes
  10. * *
* * @param Result value type * @param Future type */ static class PromiseAggregator> implements GenericFutureListener { private final Promise aggregatePromise; private Set> pendingPromises; private AtomicInteger expectedPromises = new AtomicInteger(); private AtomicInteger processedPromises = new AtomicInteger(); private boolean armed; /** * Creates a new instance. * * @param aggregatePromise the {@link Promise} to notify */ public PromiseAggregator(Promise aggregatePromise) { LettuceAssert.notNull(aggregatePromise, "AggregatePromise must not be null"); this.aggregatePromise = aggregatePromise; } /** * Add the number of {@code count} to the count of expected promises. * * @param count number of futures/promises, that is added to the overall expectation count. * @throws IllegalStateException if the aggregator was armed */ public void expectMore(int count) { LettuceAssert.assertState(!armed, "Aggregator is armed and does not allow any further expectations"); expectedPromises.addAndGet(count); } /** * Arm the aggregator to expect completion of the futures. * * @throws IllegalStateException if the aggregator was armed */ public void arm() { LettuceAssert.assertState(!armed, "Aggregator is already armed"); armed = true; } /** * Add the given {@link Promise}s to the aggregator. * * @param promises the promises * @throws IllegalStateException if the aggregator was not armed */ @SafeVarargs public final PromiseAggregator add(Promise... promises) { LettuceAssert.notNull(promises, "Promises must not be null"); LettuceAssert.assertState(armed, "Aggregator is not armed and does not allow adding promises in that state. Call arm() first."); if (promises.length == 0) { return this; } synchronized (this) { if (pendingPromises == null) { int size; if (promises.length > 1) { size = promises.length; } else { size = 2; } pendingPromises = new LinkedHashSet<>(size); } for (Promise p : promises) { if (p == null) { continue; } pendingPromises.add(p); p.addListener(this); } } return this; } @Override public synchronized void operationComplete(F future) throws Exception { if (pendingPromises == null) { aggregatePromise.setSuccess(null); } else { pendingPromises.remove(future); processedPromises.incrementAndGet(); if (!future.isSuccess()) { Throwable cause = future.cause(); aggregatePromise.setFailure(cause); for (Promise pendingFuture : pendingPromises) { pendingFuture.setFailure(cause); } } else if (processedPromises.get() == expectedPromises.get()) { if (pendingPromises.isEmpty()) { aggregatePromise.setSuccess(null); } else { throw new IllegalStateException( "Processed promises == expected promises but pending promises is not empty. This should not have happened!"); } } } } } }