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

com.twitter.distributedlog.util.FutureUtils Maven / Gradle / Ivy

The newest version!
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.twitter.distributedlog.util;

import com.twitter.distributedlog.exceptions.BKTransmitException;
import com.twitter.distributedlog.DistributedLogConstants;
import com.twitter.distributedlog.exceptions.LockingException;
import com.twitter.distributedlog.ZooKeeperClient;
import com.twitter.distributedlog.exceptions.DLInterruptedException;
import com.twitter.distributedlog.exceptions.UnexpectedException;
import com.twitter.distributedlog.exceptions.ZKException;
import com.twitter.util.Await;
import com.twitter.util.Duration;
import com.twitter.util.Function;
import com.twitter.util.Future;
import com.twitter.util.FutureCancelledException;
import com.twitter.util.FutureEventListener;
import com.twitter.util.Promise;
import com.twitter.util.Return;
import com.twitter.util.Throw;
import org.apache.bookkeeper.client.BKException;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.runtime.BoxedUnit;

import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Utilities to process future
 */
public class FutureUtils {

    private static final Logger logger = LoggerFactory.getLogger(FutureUtils.class);

    public static class OrderedFutureEventListener
            implements FutureEventListener {

        public static  OrderedFutureEventListener of(
                FutureEventListener listener,
                OrderedScheduler scheduler,
                Object key) {
            return new OrderedFutureEventListener(scheduler, key, listener);
        }

        private final OrderedScheduler scheduler;
        private final Object key;
        private final FutureEventListener listener;

        private OrderedFutureEventListener(OrderedScheduler scheduler,
                                           Object key,
                                           FutureEventListener listener) {
            this.scheduler = scheduler;
            this.key = key;
            this.listener = listener;
        }

        @Override
        public void onSuccess(final R value) {
            scheduler.submit(key, new Runnable() {
                @Override
                public void run() {
                    listener.onSuccess(value);
                }
            });
        }

        @Override
        public void onFailure(final Throwable cause) {
            scheduler.submit(key, new Runnable() {
                @Override
                public void run() {
                    listener.onFailure(cause);
                }
            });
        }
    }

    public static class FutureEventListenerRunnable
            implements FutureEventListener {

        public static  FutureEventListenerRunnable of(
                FutureEventListener listener,
                ExecutorService executorService) {
            return new FutureEventListenerRunnable(executorService, listener);
        }

        private final ExecutorService executorService;
        private final FutureEventListener listener;

        private FutureEventListenerRunnable(ExecutorService executorService,
                                            FutureEventListener listener) {
            this.executorService = executorService;
            this.listener = listener;
        }

        @Override
        public void onSuccess(final R value) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    listener.onSuccess(value);
                }
            });
        }

        @Override
        public void onFailure(final Throwable cause) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    listener.onFailure(cause);
                }
            });
        }
    }

    private static class ListFutureProcessor
            extends Function
            implements FutureEventListener, Runnable {

        private volatile boolean interrupted = false;
        private final Iterator itemsIter;
        private final Function> processFunc;
        private final Promise> promise;
        private final List results;
        private final ExecutorService callbackExecutor;

        ListFutureProcessor(List items,
                            Function> processFunc,
                            ExecutorService callbackExecutor) {
            this.itemsIter = items.iterator();
            this.processFunc = processFunc;
            this.promise = new Promise>();
            this.promise.setInterruptHandler(this);
            this.results = new ArrayList();
            this.callbackExecutor = callbackExecutor;
        }

        @Override
        public BoxedUnit apply(Throwable cause) {
            interrupted = true;
            return BoxedUnit.UNIT;
        }

        @Override
        public void onSuccess(R value) {
            results.add(value);
            if (null == callbackExecutor) {
                run();
            } else {
                callbackExecutor.submit(this);
            }
        }

        @Override
        public void onFailure(final Throwable cause) {
            if (null == callbackExecutor) {
                promise.setException(cause);
            } else {
                callbackExecutor.submit(new Runnable() {
                    @Override
                    public void run() {
                        promise.setException(cause);
                    }
                });
            }
        }

        @Override
        public void run() {
            if (interrupted) {
                logger.debug("ListFutureProcessor is interrupted.");
                return;
            }
            if (!itemsIter.hasNext()) {
                promise.setValue(results);
                return;
            }
            processFunc.apply(itemsIter.next()).addEventListener(this);
        }
    }

    /**
     * Process the list of items one by one using the process function processFunc.
     * The process will be stopped immediately if it fails on processing any one.
     *
     * @param collection list of items
     * @param processFunc process function
     * @param callbackExecutor executor to process the item
     * @return future presents the list of processed results
     */
    public static  Future> processList(List collection,
                                                     Function> processFunc,
                                                     @Nullable ExecutorService callbackExecutor) {
        ListFutureProcessor processor =
                new ListFutureProcessor(collection, processFunc, callbackExecutor);
        if (null != callbackExecutor) {
            callbackExecutor.submit(processor);
        } else {
            processor.run();
        }
        return processor.promise;
    }

    /**
     * Await for the result of the future and thrown bk related exceptions.
     *
     * @param result future to wait for
     * @return the result of future
     * @throws BKException when exceptions are thrown by the future. If there is unkown exceptions
     *         thrown from the future, the exceptions will be wrapped into
     *         {@link org.apache.bookkeeper.client.BKException.BKUnexpectedConditionException}.
     */
    public static  T bkResult(Future result) throws BKException {
        try {
            return Await.result(result);
        } catch (BKException bke) {
            throw bke;
        } catch (InterruptedException ie) {
            throw BKException.create(BKException.Code.InterruptedException);
        } catch (Exception e) {
            logger.warn("Encountered unexpected exception on waiting bookkeeper results : ", e);
            throw BKException.create(BKException.Code.UnexpectedConditionException);
        }
    }

    /**
     * Return the bk exception return code for a throwable.
     *
     * @param throwable the cause of the exception
     * @return the bk exception return code. if the exception isn't bk exceptions,
     *         it would return bk exception code.
     */
    public static int bkResultCode(Throwable throwable) {
        if (throwable instanceof BKException) {
            return ((BKException)throwable).getCode();
        }
        return BKException.Code.UnexpectedConditionException;
    }

    /**
     * Wait for the result until it completes.
     *
     * @param result result to wait
     * @return the result
     * @throws IOException when encountered exceptions on the result
     */
    public static  T result(Future result) throws IOException {
        return result(result, Duration.Top());
    }

    /**
     * Wait for the result for a given duration.
     * 

If the result is not ready within `duration`, an IOException will thrown wrapping with * corresponding {@link com.twitter.util.TimeoutException}. * * @param result result to wait * @param duration duration to wait * @return the result * @throws IOException when encountered exceptions on the result or waiting for the result. */ public static T result(Future result, Duration duration) throws IOException { try { return Await.result(result, duration); } catch (KeeperException ke) { throw new ZKException("Encountered zookeeper exception on waiting result", ke); } catch (BKException bke) { throw new BKTransmitException("Encountered bookkeeper exception on waiting result", bke.getCode()); } catch (IOException ioe) { throw ioe; } catch (InterruptedException ie) { throw new DLInterruptedException("Interrupted on waiting result", ie); } catch (Exception e) { throw new IOException("Encountered exception on waiting result", e); } } /** * Wait for the result of a lock operation. * * @param result result to wait * @param lockPath path of the lock * @return the result * @throws LockingException when encountered exceptions on the result of lock operation */ public static T lockResult(Future result, String lockPath) throws LockingException { try { return Await.result(result); } catch (LockingException le) { throw le; } catch (Exception e) { throw new LockingException(lockPath, "Encountered exception on locking ", e); } } /** * Convert the throwable to zookeeper related exceptions. * * @param throwable cause * @param path zookeeper path * @return zookeeper related exceptions */ public static Throwable zkException(Throwable throwable, String path) { if (throwable instanceof KeeperException) { return throwable; } else if (throwable instanceof ZooKeeperClient.ZooKeeperConnectionException) { return KeeperException.create(KeeperException.Code.CONNECTIONLOSS, path); } else if (throwable instanceof InterruptedException) { return new DLInterruptedException("Interrupted on operating " + path, throwable); } else { return new UnexpectedException("Encountered unexpected exception on operatiing " + path, throwable); } } /** * Cancel the future. It would interrupt the future. * * @param future future to cancel */ public static void cancel(Future future) { future.raise(new FutureCancelledException()); } /** * Raise an exception to the promise within a given timeout period. * If the promise has been satisfied before raising, it won't change the state of the promise. * * @param promise promise to raise exception * @param timeout timeout period * @param unit timeout period unit * @param cause cause to raise * @param scheduler scheduler to execute raising exception * @param key the submit key used by the scheduler * @return the promise applied with the raise logic */ public static Promise within(final Promise promise, final long timeout, final TimeUnit unit, final Throwable cause, final OrderedScheduler scheduler, final Object key) { if (timeout < DistributedLogConstants.FUTURE_TIMEOUT_IMMEDIATE || promise.isDefined()) { return promise; } scheduler.schedule(key, new Runnable() { @Override public void run() { logger.info("Raise exception", cause); // satisfy the promise FutureUtils.setException(promise, cause); } }, timeout, unit); return promise; } /** * Satisfy the promise with provide value in an ordered scheduler. *

If the promise was already satisfied, nothing will be changed. * * @param promise promise to satisfy * @param value value to satisfy * @param scheduler scheduler to satisfy the promise with provided value * @param key the submit key of the ordered scheduler */ public static void setValue(final Promise promise, final T value, OrderedScheduler scheduler, Object key) { scheduler.submit(key, new Runnable() { @Override public void run() { setValue(promise, value); } }); } /** * Satisfy the promise with provide value. *

If the promise was already satisfied, nothing will be changed. * * @param promise promise to satisfy * @param value value to satisfy * @return true if successfully satisfy the future. false if the promise has been satisfied. */ public static boolean setValue(Promise promise, T value) { boolean success = promise.updateIfEmpty(new Return(value)); if (!success) { logger.info("Result set multiple times. Value = '{}', New = 'Return({})'", promise.poll(), value); } return success; } /** * Satisfy the promise with provided cause in an ordered scheduler. * * @param promise promise to satisfy * @param throwable cause to satisfy * @param scheduler the scheduler to satisfy the promise * @param key submit key of the ordered scheduler */ public static void setException(final Promise promise, final Throwable throwable, OrderedScheduler scheduler, Object key) { scheduler.submit(key, new Runnable() { @Override public void run() { setException(promise, throwable); } }); } /** * Satisfy the promise with provided cause. * * @param promise promise to satisfy * @param cause cause to satisfy * @return true if successfully satisfy the future. false if the promise has been satisfied. */ public static boolean setException(Promise promise, Throwable cause) { boolean success = promise.updateIfEmpty(new Throw(cause)); if (!success) { logger.info("Result set multiple times. Value = '{}', New = 'Throw({})'", promise.poll(), cause); } return success; } /** * Ignore exception from the future. * * @param future the original future * @return a transformed future ignores exceptions */ public static Promise ignore(Future future) { return ignore(future, null); } /** * Ignore exception from the future and log errorMsg on exceptions * * @param future the original future * @param errorMsg the error message to log on exceptions * @return a transformed future ignores exceptions */ public static Promise ignore(Future future, final String errorMsg) { final Promise promise = new Promise(); future.addEventListener(new FutureEventListener() { @Override public void onSuccess(T value) { setValue(promise, null); } @Override public void onFailure(Throwable cause) { if (null != errorMsg) { logger.error(errorMsg, cause); } setValue(promise, null); } }); return promise; } /** * Create transmit exception from transmit result. * * @param transmitResult * transmit result (basically bk exception code) * @return transmit exception */ public static BKTransmitException transmitException(int transmitResult) { return new BKTransmitException("Failed to write to bookkeeper; Error is (" + transmitResult + ") " + BKException.getMessage(transmitResult), transmitResult); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy