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

io.streamthoughts.azkarra.api.monad.Try Maven / Gradle / Ivy

There is a newer version: 0.9.2
Show newest version
/*
 * Copyright 2019-2020 StreamThoughts.
 *
 * 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 io.streamthoughts.azkarra.api.monad;

import io.streamthoughts.azkarra.api.errors.ExecutionException;
import io.streamthoughts.azkarra.api.time.Time;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

public interface Try {

    static  Retriable retriable(final CheckedSupplier supplier, final Retry retry) {
        final long started = Time.SYSTEM.milliseconds();

        int numberOfFailedAttempts = 0;
        try {
            long timeout;
            Try failable;
            do {
                failable = failable(supplier);
                if (failable.isSuccess() || !retry.isRetriable(failable.getThrowable())) {
                    return new Retriable<>(numberOfFailedAttempts, failable);
                }
                timeout = Math.abs(retry.stopAfterMs() - (Time.SYSTEM.milliseconds() - started));
                long waitMs = Math.min(timeout, retry.fixedWaitMs());
                Thread.sleep(waitMs);
                numberOfFailedAttempts++;
            } while (numberOfFailedAttempts < retry.maxAttempts() && timeout > 0);
            return new Retriable<>(numberOfFailedAttempts, failable);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new Retriable<>(numberOfFailedAttempts, Try.failure(e));
        }
    }

    static  Try failable(final CheckedSupplier supplier) {
       try {
           return success(supplier.get());
       } catch (Throwable t) {
           return failure(t);
       }
    }

    static  Try success(V v) {
        return new Success<>(v);
    }

    static  Try failure(Throwable t) {
        return new Failure<>(t);
    }

    V get();

    boolean isSuccess();

    boolean isFailure();

    Throwable getThrowable();

     Try map(final Function mapper);

     Try flatMap(final Function> mapper);

    Try recover(final Function> f);

     Try transform(final Function> s, final Function> f);

    default Optional toOptional() {
        return isFailure() ? Optional.empty() : Optional.of(get());
    }

    class Retriable implements Try {

        private final int numberOfFailedAttempts;

        private final Try lastAttempt;

        Retriable(int numberOfFailedAttempts, final Try lastAttempt) {
            this.numberOfFailedAttempts = numberOfFailedAttempts;
            this.lastAttempt = lastAttempt;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public V get() {
            return lastAttempt.get();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isSuccess() {
            return lastAttempt.isSuccess();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isFailure() {
            return lastAttempt.isFailure();
        }

        @Override
        public Throwable getThrowable() {
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try map(final Function mapper) {
            return new Retriable<>(numberOfFailedAttempts, lastAttempt.map(mapper));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try flatMap(final Function> mapper) {
            return new Retriable<>(numberOfFailedAttempts, lastAttempt.flatMap(mapper));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Try recover(final Function> f) {
            return lastAttempt.recover(f);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try transform(final Function> s, final Function> f) {
            return lastAttempt.transform(s, f);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return "Retriable{" +
                    "numberOfFailedAttempts=" + numberOfFailedAttempts +
                    ", lastAttempt=" + lastAttempt +
                    '}';
        }
    }

    class Failure implements Try {

        private final Throwable exception;

        Failure(final Throwable exception) {
            Objects.requireNonNull(exception, "exception can't be null");
            this.exception = exception;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public V get() {
            if (exception instanceof RuntimeException) {
                throw (RuntimeException)exception;
            }
            throw new ExecutionException(exception);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isSuccess() {
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isFailure() {
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Throwable getThrowable() {
            return exception;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try map(Function mapper) {
            Objects.requireNonNull(mapper, "mapper can't be null");
            return new Try.Failure<>(exception);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try flatMap(Function> mapper) {
            Objects.requireNonNull(mapper, "mapper can't be null");
            return new Try.Failure<>(exception);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Try recover(Function> fn) {
            try {
                return fn.apply(exception);
            } catch (Throwable t) {
                return failure(t);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try transform(final Function> s, final Function> f) {
            return f.apply(exception);
        }
    }

    class Success implements Try {

        private V value;

        Success(final V value) {
            Objects.requireNonNull(value);
            this.value = value;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public V get() {
            return value;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isSuccess() {
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isFailure() {
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Throwable getThrowable() {
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try map(Function mapper) {
            Objects.requireNonNull(mapper, "mapper can't be null");
            try {
                return Try.success(mapper.apply(value));
            } catch (Throwable t) {
                return Try.failure(t);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try flatMap(Function> mapper) {
            Objects.requireNonNull(mapper, "mapper can't be null");
            try {
                return mapper.apply(get());
            } catch (Throwable t) {
                return new Failure<>(t);
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Try recover(Function> f) {
            return this;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public  Try transform(final Function> s, final Function> f) {
            return s.apply(value);
        }
    }
}