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

io.activej.service.adapter.ServiceAdapters Maven / Gradle / Ivy

There is a newer version: 6.0-beta2
Show newest version
/*
 * Copyright (C) 2020 ActiveJ LLC.
 *
 * Licensed 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.activej.service.adapter;

import io.activej.async.service.EventloopService;
import io.activej.common.service.BlockingService;
import io.activej.eventloop.Eventloop;
import io.activej.eventloop.net.BlockingSocketServer;
import io.activej.inject.binding.OptionalDependency;
import io.activej.net.EventloopServer;
import io.activej.service.Service;
import org.slf4j.Logger;

import javax.sql.DataSource;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.util.*;
import java.util.concurrent.*;

import static io.activej.eventloop.util.RunnableWithContext.wrapContext;
import static org.slf4j.LoggerFactory.getLogger;

@SuppressWarnings("WeakerAccess")
public final class ServiceAdapters {
	private static final Logger logger = getLogger(ServiceAdapters.class);

	public abstract static class SimpleServiceAdapter implements ServiceAdapter {
		private final boolean startConcurrently;
		private final boolean stopConcurrently;

		protected SimpleServiceAdapter(boolean startConcurrently, boolean stopConcurrently) {
			this.startConcurrently = startConcurrently;
			this.stopConcurrently = stopConcurrently;
		}

		protected SimpleServiceAdapter() {
			this(true, true);
		}

		protected abstract void start(S instance) throws Exception;

		protected abstract void stop(S instance) throws Exception;

		@Override
		public final CompletableFuture start(S instance, Executor executor) {
			CompletableFuture future = new CompletableFuture<>();
			(startConcurrently ? executor : (Executor) Runnable::run).execute(() -> {
				try {
					start(instance);
					future.complete(null);
				} catch (Exception e) {
					future.completeExceptionally(e);
				}
			});
			return future;
		}

		@Override
		public final CompletableFuture stop(S instance, Executor executor) {
			CompletableFuture future = new CompletableFuture<>();
			(stopConcurrently ? executor : (Executor) Runnable::run).execute(() -> {
				try {
					stop(instance);
					future.complete(null);
				} catch (Exception e) {
					future.completeExceptionally(e);
				}
			});
			return future;
		}
	}

	public static ServiceAdapter forService() {
		return new ServiceAdapter() {
			@Override
			public CompletableFuture start(Service instance, Executor executor) {
				return instance.start();
			}

			@Override
			public CompletableFuture stop(Service instance, Executor executor) {
				return instance.stop();
			}
		};
	}

	/**
	 * Returns factory which transforms blocking Service to asynchronous non-blocking CompletableFuture. It runs blocking operations from other thread from
	 * executor.
	 */
	public static ServiceAdapter forBlockingService() {
		return new SimpleServiceAdapter() {
			@Override
			protected void start(BlockingService instance) throws Exception {
				instance.start();
			}

			@Override
			protected void stop(BlockingService instance) throws Exception {
				instance.stop();
			}
		};
	}

	public static  ServiceAdapter> forOptionalDependency(ServiceAdapter adapter) {
		return new ServiceAdapter>() {
			@Override
			public CompletableFuture start(OptionalDependency optional, Executor executor) {
				if (optional.isPresent()) {
					T instance = optional.get();
					return adapter.start(instance, executor);
				}
				return CompletableFuture.completedFuture(null);
			}

			@Override
			public CompletableFuture stop(OptionalDependency optional, Executor executor) {
				if (optional.isPresent()) {
					T instance = optional.get();
					return adapter.stop(instance, executor);
				}
				return CompletableFuture.completedFuture(null);
			}
		};
	}

	/**
	 * Returns factory which transforms Timer to CompletableFuture. On start it does nothing, on stop it cancel timer.
	 */
	public static ServiceAdapter forTimer() {
		return new SimpleServiceAdapter(false, false) {
			@Override
			protected void start(Timer instance) {
			}

			@Override
			protected void stop(Timer instance) {
				instance.cancel();
			}
		};
	}

	/**
	 * Returns factory which transforms ExecutorService to CompletableFuture. On starting it doing nothing, on stopping it shuts down ExecutorService.
	 */
	public static ServiceAdapter forExecutorService() {
		return new SimpleServiceAdapter(false, true) {
			@Override
			protected void start(ExecutorService instance) {
			}

			@Override
			protected void stop(ExecutorService instance) throws Exception {
				instance.shutdown();
				if (!instance.isTerminated()) {
					logger.warn("Awaiting termination of {} ...", instance);
					//noinspection ResultOfMethodCallIgnored
					instance.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
					logger.info("Instance {} has been terminated", instance);
				}
			}
		};
	}

	/**
	 * Returns factory which transforms Closeable object to CompletableFuture. On starting it doing nothing, on stopping it close Closeable.
	 */
	public static ServiceAdapter forCloseable() {
		return new SimpleServiceAdapter(false, true) {
			@Override
			protected void start(Closeable instance) {
			}

			@Override
			protected void stop(Closeable instance) throws Exception {
				instance.close();
			}
		};
	}

	/**
	 * Returns factory which transforms DataSource object to CompletableFuture. On start it checks connecting , on stop it closes DataSource.
	 */
	public static ServiceAdapter forDataSource() {
		return new SimpleServiceAdapter(true, false) {
			@Override
			protected void start(DataSource instance) throws Exception {
				Connection connection = instance.getConnection();
				connection.close();
			}

			@Override
			protected void stop(DataSource instance) {
			}
		};
	}

	public static ServiceAdapter forEventloopService() {
		return new ServiceAdapter() {
			@Override
			public CompletableFuture start(EventloopService instance, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				instance.getEventloop().execute(wrapContext(instance, () -> {
					try {
						instance.start()
								.whenResult(future::complete)
								.whenException(future::completeExceptionally);
					} catch (Exception e) {
						future.completeExceptionally(e);
					}
				}));
				return future;
			}

			@Override
			public CompletableFuture stop(EventloopService instance, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				instance.getEventloop().execute(wrapContext(instance, () -> {
					try {
						instance.stop()
								.whenResult(future::complete)
								.whenException(future::completeExceptionally);
					} catch (Exception e) {
						future.completeExceptionally(e);
					}
				}));
				return future;
			}
		};
	}

	public static ServiceAdapter forEventloopServer() {
		return new ServiceAdapter() {
			@Override
			public CompletableFuture start(EventloopServer instance, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				instance.getEventloop().execute(wrapContext(instance, () -> {
					try {
						instance.listen();
						future.complete(null);
					} catch (IOException e) {
						future.completeExceptionally(e);
					}
				}));
				return future;
			}

			@Override
			public CompletableFuture stop(EventloopServer instance, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				instance.getEventloop().execute(wrapContext(instance, () -> instance.close()
						.whenResult(future::complete)
						.whenException(future::completeExceptionally)));
				return future;
			}
		};
	}

	public static ServiceAdapter forEventloop(ThreadFactory threadFactory) {
		return new ServiceAdapter() {
			@Override
			public CompletableFuture start(Eventloop eventloop, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				threadFactory.newThread(() -> {
					eventloop.keepAlive(true);
					future.complete(null);
					eventloop.run();
				}).start();
				return future;
			}

			@Override
			public CompletableFuture stop(Eventloop eventloop, Executor executor) {
				Thread eventloopThread = eventloop.getEventloopThread();
				if (eventloopThread == null) {
					// already stopped
					return CompletableFuture.completedFuture(null);
				}
				CompletableFuture future = new CompletableFuture<>();
				eventloop.execute(() -> {
					eventloop.keepAlive(false);
					logStopping(eventloop);
					Eventloop.logger.info("Waiting for {}", eventloop);
				});
				executor.execute(() -> {
					try {
						eventloopThread.join();
						future.complete(null);
					} catch (InterruptedException e) {
						Thread.currentThread().interrupt();
						future.completeExceptionally(e);
					}
				});
				return future;
			}

			private void logStopping(Eventloop eventloop) {
				eventloop.delayBackground(1000L, () -> {
					if (eventloop.getEventloopThread() != null) {
						Eventloop.logger.info("...Waiting for {}", eventloop);
						logStopping(eventloop);
					}
				});
			}
		};
	}

	public static ServiceAdapter forEventloop() {
		ThreadFactory threadFactory = Executors.defaultThreadFactory();
		return forEventloop(r -> {
			Thread thread = threadFactory.newThread(r);
			thread.setName("eventloop: " + thread.getName());
			return thread;
		});
	}

	public static ServiceAdapter forBlockingSocketServer() {
		return new ServiceAdapters.SimpleServiceAdapter() {
			@Override
			protected void start(BlockingSocketServer instance) throws Exception {
				instance.start();
			}

			@Override
			protected void stop(BlockingSocketServer instance) throws Exception {
				instance.stop();
			}
		};
	}

	public static  ServiceAdapter immediateServiceAdapter() {
		return new SimpleServiceAdapter(false, false) {
			@Override
			protected void start(T instance) {
			}

			@Override
			protected void stop(T instance) {
			}
		};
	}

	@SafeVarargs
	public static > ServiceAdapter combinedAdapter(S... startOrder) {
		return combinedAdapter(Arrays.asList(startOrder));
	}

	public static  ServiceAdapter combinedAdapter(List> startOrder) {
		List> stopOrder = new ArrayList<>(startOrder);
		Collections.reverse(stopOrder);
		return combinedAdapter(startOrder, stopOrder);
	}

	@FunctionalInterface
	private interface Action {
		CompletableFuture doAction(ServiceAdapter serviceAdapter, T instance, Executor executor);
	}

	public static  ServiceAdapter combinedAdapter(List> startOrder,
			List> stopOrder) {
		return new ServiceAdapter() {
			@SuppressWarnings("unchecked")
			private void doAction(T instance, Executor executor,
					Iterator> iterator, CompletableFuture future,
					Action action) {
				if (iterator.hasNext()) {
					action.doAction((ServiceAdapter) iterator.next(), instance, executor)
							.whenCompleteAsync(($, e) -> {
								if (e == null) {
									doAction(instance, executor, iterator, future, action);
								} else if (e instanceof InterruptedException) {
									Thread.currentThread().interrupt();
									future.completeExceptionally(e);
								} else if (e instanceof ExecutionException) {
									future.completeExceptionally(e.getCause());
								}
							}, Runnable::run);
				} else {
					future.complete(null);
				}
			}

			@Override
			public CompletableFuture start(T instance, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				doAction(instance, executor, startOrder.iterator(), future,
						ServiceAdapter::start);
				return future;
			}

			@Override
			public CompletableFuture stop(T instance, Executor executor) {
				CompletableFuture future = new CompletableFuture<>();
				doAction(instance, executor, stopOrder.iterator(), future,
						ServiceAdapter::stop);
				return future;
			}
		};
	}
}