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

com.couchbase.client.java.util.Blocking Maven / Gradle / Ivy

/**
 * Copyright (C) 2014 Couchbase, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
 * IN THE SOFTWARE.
 */
package com.couchbase.client.java.util;

import com.couchbase.client.core.annotations.InterfaceAudience;
import com.couchbase.client.core.annotations.InterfaceStability;
import rx.Observable;
import rx.Subscriber;
import rx.observables.BlockingObservable;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Contains various utility methods related to blocking operations.
 *
 * @author Michael Nitschinger
 * @since 2.0.2
 */
@InterfaceStability.Experimental
@InterfaceAudience.Private
public class Blocking {

    /**
     * Blocks on an {@link Observable} and returns a single event or throws an {@link Exception}.
     *
     * Note that when this method is used, only the first item emitted will be returned. The caller needs to make
     * sure that the source {@link Observable} only ever returns a single item (or none). The {@link BlockingObservable}
     * code applies different operators like single, last, first and more, these need to be applied manually.
     *
     * This code is based on {@link BlockingObservable#blockForSingle}, but does not wait forever. Instead, it
     * utilizes the internal {@link CountDownLatch} to optimize the timeout case, with less GC and CPU overhead
     * than chaining in an {@link Observable#timeout(long, TimeUnit)} operator.
     *
     * If an error happens inside the {@link Observable}, it will be raised as an {@link Exception}. If the timeout
     * kicks in, a {@link TimeoutException} nested in a {@link RuntimeException} is thrown to be fully compatible
     * with the {@link Observable#timeout(long, TimeUnit)} behavior.
     *
     * @param observable the source {@link Observable}
     * @param timeout the maximum timeout before an exception is thrown.
     * @param tu the timeout unit.
     * @param  the type returned.
     * @return the extracted value from the {@link Observable} or throws in an error case.
     */
    public static  T blockForSingle(final Observable observable, final long timeout,
        final TimeUnit tu) {
        final CountDownLatch latch = new CountDownLatch(1);
        TrackingSubscriber subscriber = new TrackingSubscriber(latch);

        observable.subscribe(subscriber);

        try {
            if (!latch.await(timeout, tu)) {
                throw new RuntimeException(new TimeoutException());
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Interrupted while waiting for subscription to complete.", e);
        }

        if (subscriber.returnException() != null) {
            if (subscriber.returnException() instanceof RuntimeException) {
                throw (RuntimeException) subscriber.returnException();
            } else {
                throw new RuntimeException(subscriber.returnException());
            }
        }

        return subscriber.returnItem();
    }


    /**
     * A {@link Subscriber} which tracks the returned item or exception.
     *
     * By pushing out the {@link Subscriber} in it's own class, the code
     * can get rid of {@link AtomicReference} objects and stick with
     * plain volatiles instead (since it just needs get/set semantics) and
     * reduce allocations a little bit.
     *
     * @since 2.2.0
     */
    private final static class TrackingSubscriber extends Subscriber {

        private final CountDownLatch latch;
        private volatile T returnItem = null;
        private volatile Throwable returnException = null;

        public TrackingSubscriber(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void onCompleted() {
            latch.countDown();
        }

        @Override
        public void onError(final Throwable e) {
            returnException = e;
            latch.countDown();
        }

        @Override
        public void onNext(final T item) {
            returnItem = item;
        }

        public Throwable returnException() {
            return returnException;
        }

        public T returnItem() {
            return returnItem;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy