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
/*******************************************************************************
 *   © 2019-2024 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();
		return 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 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