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

io.activej.launchers.initializers.Initializers Maven / Gradle / Ivy

/*
 * 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.launchers.initializers;

import io.activej.async.service.TaskScheduler;
import io.activej.common.MemSize;
import io.activej.common.annotation.StaticFactories;
import io.activej.common.initializer.Initializer;
import io.activej.config.Config;
import io.activej.eventloop.Eventloop;
import io.activej.eventloop.inspector.ThrottlingController;
import io.activej.http.HttpServer;
import io.activej.inject.Key;
import io.activej.jmx.JmxModule;
import io.activej.launcher.Launcher;
import io.activej.net.AbstractReactiveServer;
import io.activej.net.PrimaryServer;
import io.activej.trigger.TriggerResult;
import io.activej.trigger.TriggersModuleSettings;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

import static io.activej.config.converter.ConfigConverters.*;
import static io.activej.launchers.initializers.TriggersHelper.ofPromiseStatsLastSuccess;
import static io.activej.trigger.Severity.*;

@StaticFactories(Initializer.class)
public class Initializers {
	public static final String GLOBAL_EVENTLOOP_NAME = "GlobalEventloopStats";
	public static final Key GLOBAL_EVENTLOOP_KEY = Key.of(Eventloop.class, GLOBAL_EVENTLOOP_NAME);

	public static > Initializer ofAbstractServer(Config config) {
		return builder -> builder
			.withListenAddresses(config.get(ofList(ofInetSocketAddress()), "listenAddresses"))
			.withAcceptOnce(config.get(ofBoolean(), "acceptOnce", false))
			.setIfNotNull(
				AbstractReactiveServer.Builder::withSocketSettings,
				config.get(ofSocketSettings(), "socketSettings", null)
			)
			.setIfNotNull(
				AbstractReactiveServer.Builder::withServerSocketSettings,
				config.get(ofServerSocketSettings(), "serverSocketSettings", null)
			);
	}

	public static Initializer ofPrimaryServer(Config config) {
		return ofAbstractServer(config);
	}

	public static Initializer ofEventloop(Config config) {
		return builder -> builder
			.setIfNotNull(
				Eventloop.Builder::withFatalErrorHandler,
				config.get(ofFatalErrorHandler(), "fatalErrorHandler", null)
			)
			.setIfNotNull(
				Eventloop.Builder::withIdleInterval,
				config.get(ofDuration(), "idleInterval", null)
			)
			.setIfNotNull(
				Eventloop.Builder::withThreadPriority,
				config.get(ofInteger(), "threadPriority", null)
			);
	}

	public static Initializer ofTaskScheduler(Config config) {
		return builder -> builder
			.withEnabled(!config.get(ofBoolean(), "disabled", false))
			.withAbortOnError(config.get(ofBoolean(), "abortOnError", false))
			.withInitialDelay(config.get(ofDuration(), "initialDelay", Duration.ZERO))
			.withSchedule(config.get(ofReactorTaskSchedule(), "schedule"))
			.withRetryPolicy(config.get(ofRetryPolicy(), "retryPolicy"));
	}

	public static Initializer ofHttpServer(Config config) {
		return builder -> builder
			.initialize(ofAbstractServer(config))
			.initialize(ofHttpWorker(config));
	}

	public static Initializer ofHttpWorker(Config config) {
		return builder -> builder
			.withKeepAliveTimeout(config.get(ofDuration(), "keepAliveTimeout", HttpServer.KEEP_ALIVE_TIMEOUT))
			.withReadWriteTimeout(config.get(ofDuration(), "readWriteTimeout", HttpServer.READ_WRITE_TIMEOUT))
			.withMaxBodySize(config.get(ofMemSize(), "maxBodySize", MemSize.ZERO));
	}

	public static Initializer ofGlobalEventloopStats() {
		return builder -> builder
			.withGlobalMBean(Eventloop.class, GLOBAL_EVENTLOOP_KEY)
			.withOptional(GLOBAL_EVENTLOOP_KEY, "fatalErrors_total")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "businessLogicTime_smoothedAverage")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "loops_totalCount")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "loops_smoothedRate")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "idleLoops_totalCount")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "idleLoops_smoothedRate")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "selectOverdues_totalCount")
			.withOptional(GLOBAL_EVENTLOOP_KEY, "selectOverdues_smoothedRate");
	}

	public static Initializer ofLauncherTriggers(Duration maxRunDelay) {
		return triggersSettings -> triggersSettings
			.with(Launcher.class, AVERAGE, "runDelay", launcher ->
				TriggerResult.ofValue(launcher.getDurationOfStart(),
					launcher.getInstantOfRun() == null &&
					launcher.getDurationOfStart() != null &&
					launcher.getDurationOfStart().toMillis() > maxRunDelay.toMillis()));
	}

	public static Initializer ofEventloopFatalErrorsTriggers() {
		return triggersSettings -> triggersSettings
			.with(Eventloop.class, HIGH, "fatalErrors", eventloop ->
				eventloop.getStats() == null ?
					TriggerResult.none() :
					TriggerResult.ofError(eventloop.getStats().getFatalErrors()));
	}

	public static Initializer ofEventloopBusinessLogicTriggers(Config config) {
		long businessLogicTimeLow = config.get(ofDurationAsMillis(), "businessLogicTimeLow", 10L);
		long businessLogicTimeHigh = config.get(ofDurationAsMillis(), "businessLogicTimeHigh", 100L);

		return triggersSettings -> triggersSettings
			.with(Eventloop.class, WARNING, "businessLogic", eventloop ->
				eventloop.getStats() == null ?
					TriggerResult.none() :
					TriggerResult.ofValue(eventloop.getStats().getBusinessLogicTime().getSmoothedAverage(), businessLogicTime -> businessLogicTime > businessLogicTimeLow))
			.with(Eventloop.class, HIGH, "businessLogic", eventloop ->
				eventloop.getStats() == null ?
					TriggerResult.none() :
					TriggerResult.ofValue(eventloop.getStats().getBusinessLogicTime().getSmoothedAverage(), businessLogicTime -> businessLogicTime > businessLogicTimeHigh));
	}

	public static Initializer ofThrottlingControllerTriggers(Config config) {
		double throttlingLow = config.get(ofDouble(), "throttlingLow", 0.1);
		double throttlingHigh = config.get(ofDouble(), "throttlingHigh", 0.5);

		return triggersSettings -> triggersSettings
			.with(ThrottlingController.class, WARNING, "throttling", throttlingController ->
				TriggerResult.ofValue(throttlingController == null ? null : throttlingController.getAvgThrottling(),
					throttling -> throttling > throttlingLow))
			.with(ThrottlingController.class, HIGH, "throttling", throttlingController ->
				TriggerResult.ofValue(throttlingController == null ? null : throttlingController.getAvgThrottling(),
					throttling -> throttling > throttlingHigh));
	}

	public static Initializer ofReactorTaskSchedulerTriggers(Config config) {
		Long delayError = config.get(ofDurationAsMillis(), "scheduler.delayError", null);
		Long delayWarning = config.get(ofDurationAsMillis(), "scheduler.delayWarning", null);

		return triggersSettings -> triggersSettings
			.with(TaskScheduler.class, WARNING, "error", scheduler ->
				TriggerResult.ofError(scheduler.getLastException()))
			.with(TaskScheduler.class, INFORMATION, "error", scheduler ->
				ofPromiseStatsLastSuccess(scheduler.getStats()))

			.with(TaskScheduler.class, WARNING, "delay", scheduler -> {
				Duration currentDuration = scheduler.getStats().getCurrentDuration();
				Duration duration = getDuration(scheduler);
				if (currentDuration == null || duration == null) {
					return TriggerResult.none();
				}
				return TriggerResult.ofInstant(scheduler.getStats().getLastStartTime(),
					currentDuration.toMillis() > (delayWarning != null ? delayWarning : duration.toMillis() * 3));
			})
			.with(TaskScheduler.class, HIGH, "delay", scheduler -> {
				Duration currentDuration = scheduler.getStats().getCurrentDuration();
				Duration duration = getDuration(scheduler);
				if (currentDuration == null || duration == null) {
					return TriggerResult.none();
				}
				return TriggerResult.ofInstant(scheduler.getStats().getLastStartTime(),
					currentDuration.toMillis() > (delayError != null ? delayError : duration.toMillis() * 10));
			});
	}

	public static Initializer renamedClassNames(Map, String> classToOldName) {
		Map> byPackageAndClassMap = new HashMap<>();

		for (Map.Entry, String> entry : classToOldName.entrySet()) {
			Class cls = entry.getKey();
			byPackageAndClassMap
				.computeIfAbsent(cls.getPackageName(), $ -> new HashMap<>())
				.put(cls.getSimpleName(), entry.getValue());
		}

		return builder ->
			builder.withObjectNameMapping(protoObjectName -> {
				Map packageMap = byPackageAndClassMap.get(protoObjectName.packageName());
				if (packageMap == null) return protoObjectName;

				String oldClassName = packageMap.get(protoObjectName.className());

				if (oldClassName == null) return protoObjectName;

				int lastDotIndex = oldClassName.lastIndexOf(".");

				if (lastDotIndex == -1) {
					return protoObjectName.withClassName(oldClassName);
				}

				String packageName = oldClassName.substring(0, lastDotIndex);
				String className = oldClassName.substring(lastDotIndex + 1);

				return protoObjectName
					.withPackageName(packageName)
					.withClassName(className);
			});
	}

	private static Duration getDuration(TaskScheduler scheduler) {
		return scheduler.getPeriod() != null ? scheduler.getPeriod() : scheduler.getInterval();
	}
}