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

com.sap.cloud.mt.tools.AsyncPolling Maven / Gradle / Ivy

There is a newer version: 3.3.1
Show newest version
/*
 * *************************************************************************
 *  * (C) 2019-2021 SAP SE or an SAP affiliate company. All rights reserved. *
 *  *************************************************************************
 */

package com.sap.cloud.mt.tools;

import java.time.Duration;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Class that provides an asynchronous polling functionality executed in an own thread.
 */
public class AsyncPolling {
    private final Duration delay;
    private final Duration period;
    private final Duration maximumRuntime;
    private final ConcurrentHashMap> futures = new ConcurrentHashMap<>();

    private AsyncPolling(Duration delay, Duration period, Duration maximumRuntime) {
        this.delay = delay;
        this.period = period;
        this.maximumRuntime = maximumRuntime;
    }

    /**
     * Start the polling operation
     * @param action      is a lambda expression that is executed periodically until the operation is finished or the maximum runtime is reached
     * @param finalAction is an optional lambda expression called after the polling.
     * @param          is the type of the result
     */
    public  void execute(Supplier> action, Consumer> finalAction) {
        final PollingResponse pollingResponse = new PollingResponse<>();
        UUID futureUuid = UUID.randomUUID();
        TimerTask task = createNewTask(action, finalAction, pollingResponse, futureUuid);
        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        futures.put(futureUuid, executorService.scheduleAtFixedRate(task, delay.toNanos(), period.toNanos(), TimeUnit.NANOSECONDS));
    }

    private  TimerTask createNewTask(Supplier> action, Consumer> finalAction,
                                        PollingResponse pollingResponse, UUID futureUuid) {
        final long startTime = System.currentTimeMillis();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                pollingResponse.copyFrom(action.get());
                if (pollingResponse.isFinished() || System.currentTimeMillis() - startTime >= maximumRuntime.toMillis()) {
                    if (finalAction != null) {
                        finalAction.accept(pollingResponse);
                    }
                    //The future is set externally, after scheduleAtFixedRate is called. Therefore make sure that it is set
                    Wait wait = Wait.createBuilder().waitTime(Duration.ofMillis(1))
                            .maximumTime(Duration.ofMillis(1000)).build();
                    wait.waitUntil(() -> futures.contains(futureUuid));
                    ScheduledFuture future = futures.get(futureUuid);
                    futures.remove(futureUuid);
                    //if future is still null, then we live with the runtime exception
                    future.cancel(false);
                }
            }
        };
        return task;
    }

    /**
     * @return a builder for the asynchronous polling class
     */
    public static AsyncPollingBuilder createBuilder() {
        return AsyncPollingBuilder.create();
    }

    /**
     * Builder for the asynchronous builder
     */
    public static final class AsyncPollingBuilder {
        private Duration delay;
        private Duration period;
        private Duration maximumRuntime;

        private static AsyncPollingBuilder create() {
            return new AsyncPollingBuilder();
        }

        /**
         * @param delay is the time after which the action is called the first time
         * @return builder
         */
        public AsyncPollingBuilder delay(Duration delay) {
            this.delay = delay;
            return this;
        }

        /**
         * @param period is the time interval after which the action is called again
         * @return builder
         */
        public AsyncPollingBuilder period(Duration period) {
            this.period = period;
            return this;
        }

        /**
         * @param maximumRuntime is the maximum time the polling is tried
         * @return builder
         */
        public AsyncPollingBuilder maximumRuntime(Duration maximumRuntime) {
            this.maximumRuntime = maximumRuntime;
            return this;
        }

        /**
         * @return the asynchronous builder
         */
        public AsyncPolling build() {
            return new AsyncPolling(delay, period, maximumRuntime);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy