
io.mangoo.core.Application Maven / Gradle / Ivy
package io.mangoo.core;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.parser.CronParser;
import com.google.inject.*;
import com.google.inject.Module;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.github.classgraph.*;
import io.mangoo.admin.AdminController;
import io.mangoo.async.EventBus;
import io.mangoo.async.Subscriber;
import io.mangoo.cache.CacheProvider;
import io.mangoo.constants.CacheName;
import io.mangoo.constants.Default;
import io.mangoo.constants.Key;
import io.mangoo.constants.NotNull;
import io.mangoo.enums.Mode;
import io.mangoo.enums.Sort;
import io.mangoo.interfaces.MangooBootstrap;
import io.mangoo.persistence.interfaces.Datastore;
import io.mangoo.routing.Bind;
import io.mangoo.routing.On;
import io.mangoo.routing.Router;
import io.mangoo.routing.handlers.*;
import io.mangoo.routing.routes.FileRoute;
import io.mangoo.routing.routes.PathRoute;
import io.mangoo.routing.routes.RequestRoute;
import io.mangoo.routing.routes.ServerSentEventRoute;
import io.mangoo.scheduler.CronTask;
import io.mangoo.scheduler.Schedule;
import io.mangoo.scheduler.Scheduler;
import io.mangoo.scheduler.Task;
import io.mangoo.utils.ByteUtils;
import io.mangoo.utils.MangooUtils;
import io.mangoo.utils.PersistenceUtils;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.UndertowOptions;
import io.undertow.server.HttpHandler;
import io.undertow.server.RoutingHandler;
import io.undertow.server.handlers.PathHandler;
import io.undertow.server.handlers.resource.ClassPathResourceManager;
import io.undertow.server.handlers.resource.ResourceHandler;
import io.undertow.util.Methods;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public final class Application {
private static final Logger LOG = LogManager.getLogger(Application.class);
private static final LocalDateTime START = LocalDateTime.now();
private static final int KEY_MIN_BIT_LENGTH = 512;
private static final int PASETO_SECRET_MIN_LENGTH = 256;
private static final String COLLECTION = "io.mangoo.annotations.Collection";
private static final String INDEXED = "io.mangoo.annotations.Indexed";
private static final String SCHEDULER = "io.mangoo.annotations.Run";
private static final String MODULE_CLASS = "app.Module";
private static final String ALL_PACKAGES = "*";
private static final String LOGO = """
___ __ ___ \s
_ __ ___ __ _ _ __ __ _ ___ ___ |_ _| / / / _ \\\s
| '_ ` _ \\ / _` || '_ \\ / _` | / _ \\ / _ \\ | | / / | | | |
| | | | | || (_| || | | || (_| || (_) || (_) | | | / / | |_| |
|_| |_| |_| \\__,_||_| |_| \\__, | \\___/ \\___/ |___|/_/ \\___/\s
|___/ \s""";
private static io.mangoo.core.Module module;
private static ScheduledExecutorService scheduledExecutorService;
private static ExecutorService executorService;
private static String httpHost;
private static String ajpHost;
private static Undertow undertow;
private static Mode mode;
private static Injector injector;
private static PathHandler pathHandler;
private static boolean started;
private static int httpPort;
private static int ajpPort;
private Application() {
}
public static void main(String... args) {
start(Mode.PROD);
}
@SuppressWarnings({"StatementWithEmptyBody", "LoopConditionNotUpdatedInsideLoop"})
public static void start(Mode mode) {
Objects.requireNonNull(mode, NotNull.MODE);
if (!started) {
userCheck();
prepareMode(mode);
prepareInjector();
applicationInitialized();
prepareConfig();
Thread scan = Thread.ofVirtual().start(() -> {
try (var scanResult = scanClasspath()) {
prepareScheduler(scanResult);
prepareDatastore(scanResult);
prepareSubscriber(scanResult);
}
});
prepareRoutes();
createRoutes();
validateUrls();
prepareUndertow();
prepareShutdown();
sanityChecks();
do {} while (scan.isAlive()); //NOSONAR
applicationStarted();
showLogo();
started = true;
}
}
private static void validateUrls() {
if (!Router.validUrls()) {
failsafe();
}
}
/**
* Schedules all tasks annotated with @Run
*/
private static void prepareScheduler(ScanResult scanResult) {
var config = getInstance(Config.class);
if (config.isSchedulerEnabled()) {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
executorService = Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory());
scanResult.getClassesWithMethodAnnotation(SCHEDULER).forEach(classInfo ->
classInfo.getMethodInfo().forEach(methodInfo -> {
if (!methodInfo.getAnnotationInfo().isEmpty()) {
var isCron = false;
long seconds = 0;
String at = null;
for (var i = 0; i < methodInfo.getAnnotationInfo().size(); i++) {
var annotationInfo = methodInfo.getAnnotationInfo().get(i);
at = ((String) annotationInfo
.getParameterValues(true).get("at").getValue())
.toLowerCase(Locale.ENGLISH)
.trim();
if (at.contains("every")) {
at = at.replace("every", Strings.EMPTY).trim();
var timespan = at.substring(0, at.length() - 1);
var duration = at.substring(at.length() - 1);
seconds = getSeconds(timespan, duration);
} else {
isCron = true;
}
}
if (StringUtils.isNotBlank(at)) {
schedule(classInfo, methodInfo, isCron, seconds, at);
}
}
})
);
}
}
/**
* Parses a given time span and duration and returns the number of
* matching seconds to schedule a task
*
* @param timespan The timespan to use
* @param duration The duration to use for calculation
*
* @return The duration in seconds
*/
private static long getSeconds(String timespan, String duration) {
Objects.requireNonNull(timespan, "timespan can not be null");
Objects.requireNonNull(duration, "duration can not be null");
var time = Long.parseLong(timespan);
return switch(duration) {
case "m" -> time * 60;
case "h" -> time * 3600;
case "d" -> time * 86400;
default -> time;
};
}
/**
* Schedules a task within the scheduler
*
* @param classInfo The classInfo containing the class which holds the method to execute
* @param methodInfo The methodInfo containing the method to execute
* @param isCron True if Task is a cron or false if it has a fixed rate
* @param time The fixed rate for the scheduled task to be executed
* @param at The cron expression to be used when scheduling a cron
*/
private static void schedule(ClassInfo classInfo, MethodInfo methodInfo, boolean isCron, long time, String at) {
Objects.requireNonNull(classInfo, "classInfo can not be null");
Objects.requireNonNull(methodInfo, "methodInfo can not be null");
Objects.requireNonNull(at, "at can not be null");
try {
getInstance(classInfo.loadClass());
} catch (Exception e) {
LOG.error("Failed to scheduled a task as class creation ran into an error. Check class '{}' with method '{}'", classInfo.getName(), methodInfo.getName(), e);
failsafe();
}
if (isCron) {
try {
var parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX));
var quartzCron = parser.parse(at);
quartzCron.validate();
var cronTask = new CronTask(classInfo.loadClass(), methodInfo.getName(), at);
ScheduledFuture> scheduledFuture = scheduledExecutorService.schedule(() -> executorService.submit(cronTask), 0, TimeUnit.SECONDS);
getInstance(Scheduler.class).addSchedule(Schedule.of(classInfo.loadClass().toString(), methodInfo.getName(), at, scheduledFuture, true));
LOG.info("Successfully scheduled cron task from class '{}' with method '{}' and cron '{}'", classInfo.getName(), methodInfo.getName(), at);
} catch (IllegalArgumentException e) {
LOG.error("Scheduled cron task found, but the unix cron is invalid", e);
failsafe();
}
} else {
if (time > 0) {
var task = new Task(classInfo.loadClass(), methodInfo.getName());
ScheduledFuture> scheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> executorService.submit(task), time, time, TimeUnit.SECONDS);
getInstance(Scheduler.class).addSchedule(Schedule.of(classInfo.loadClass().toString(), methodInfo.getName(), "every " + at, scheduledFuture, false));
LOG.info("Successfully scheduled task from class '{}' with method '{}' at rate 'Every {}'", classInfo.getName(), methodInfo.getName(), at);
} else {
LOG.error("Scheduled task found, but unable to schedule it. Check class '{}' with method '{}' at rate 'Every {}'", classInfo.getName(), methodInfo.getName(), at);
failsafe();
}
}
}
/**
* Configures persistence
*/
private static void prepareDatastore(ScanResult scanResult) {
var config = getInstance(Config.class);
if (config.isPersistenceEnabled()) {
scanResult.getClassesWithAnnotation(COLLECTION).forEach(classInfo -> {
String key = classInfo.getName();
AnnotationInfoList annotationInfo = classInfo.getAnnotationInfo();
String value = (String) annotationInfo.getFirst().getParameterValues().getFirst().getValue();
PersistenceUtils.addCollection(key, value);
});
Datastore datastore = Application.getInstance(Datastore.class);
scanResult.getClassesWithFieldAnnotation(INDEXED).forEach(classInfo -> {
var fieldInfoList = classInfo.getFieldInfo();
fieldInfoList.stream()
.filter(info -> info.getAnnotationInfo().size() == 1)
.filter(info -> StringUtils.isNotBlank(info.getName()))
.forEach(info -> {
List annotationParams = info.getAnnotationInfo().getFirst().getParameterValues();
boolean unique = (annotationParams.size() > 1) && (boolean) annotationParams.get(1).getValue();
var sortOrder = annotationParams.get(0).getValue().toString();
if (Sort.ASCENDING.value().equals(sortOrder)) {
datastore.addIndex(classInfo.loadClass(), Indexes.ascending(info.getName()), new IndexOptions().unique(unique));
} else if (Sort.DESCENDING.value().equals(sortOrder)) {
datastore.addIndex(classInfo.loadClass(), Indexes.descending(info.getName()), new IndexOptions().unique(unique));
}
});
});
}
}
@SuppressWarnings("unchecked")
private static void prepareSubscriber(ScanResult scanResult) {
scanResult.getClassesImplementing(Subscriber.class).forEach(classInfo -> {
var methodInfo = classInfo.getMethodInfo().getFirst();
if (("receive").equals(methodInfo.getName())) {
var methodParameterInfo = Arrays.asList(methodInfo.getParameterInfo()).getFirst();
var descriptor = methodParameterInfo.getTypeDescriptor().toString();
Application.getInstance(EventBus.class).register(descriptor, classInfo.loadClass());
}
});
}
/**
* Checks if application is run as root
*
* (Hint: There is no need to run as root)
*/
private static void userCheck() {
String osName = System.getProperty("os.name");
if (StringUtils.isNotBlank(osName) && !osName.startsWith("Windows")) {
String [] command = {"id", "-u"};
try {
Process exec = Runtime.getRuntime().exec(command);
var input = new BufferedReader(new InputStreamReader(exec.getInputStream(), StandardCharsets.UTF_8));
String output = input.lines().collect(Collectors.joining(System.lineSeparator()));
input.close();
if (("0").equals(output) && inProdMode()) {
LOG.error("Can not run application as root");
failsafe();
}
} catch (IOException e) {
LOG.error("Failed to check if application is started as root", e);
}
}
}
/**
* Checks if the application is running in dev mode
*
* @return True if the application is running in dev mode, false otherwise
*/
public static boolean inDevMode() {
return Mode.DEV == mode;
}
/**
* Checks if the application is running in prod mode
*
* @return True if the application is running in prod mode, false otherwise
*/
public static boolean inProdMode() {
return Mode.PROD == mode;
}
/**
* Checks if the application is running in test mode
*
* @return True if the application is running in test mode, false otherwise
*/
public static boolean inTestMode() {
return Mode.TEST == mode;
}
/**
* Returns the current mode the application is running in
*
* @return Enum Mode
*/
public static Mode getMode() {
return mode;
}
/**
* Returns the ScheduledExecutorService where all tasks are scheduled
*
* @return ScheduledExecutorService
*/
public static ScheduledExecutorService getScheduledExecutorService() {
return scheduledExecutorService;
}
/**
* Returns the ExecutorService the execution of the scheduled tasks
* are performed
*
* @return ExecutorService
*/
public static ExecutorService getExecutorService() {
return executorService;
}
/**
* Returns the Google Guice Injector
*
* @return Google Guice injector instance
*/
public static Injector getInjector() {
return injector;
}
/**
* @return True if the application started successfully, false otherwise
*/
public static boolean isStarted() {
return started;
}
/**
* @return The LocalDateTime of the application start
*/
public static LocalDateTime getStart() {
return START;
}
/**
* @return The duration of the application uptime
*/
public static Duration getUptime() {
return Duration.between(START, LocalDateTime.now());
}
/**
* Short form for getting a Google Guice injected class by
* calling getInstance(...)
*
* @param clazz The class to retrieve from the injector
* @param JavaDoc requires this (just ignore it)
*
* @return An instance of the requested class
*/
public static T getInstance(Class clazz) {
Objects.requireNonNull(clazz, NotNull.CLASS);
return injector.getInstance(clazz);
}
/**
* Stops the underlying undertow server
*/
public static void stopUndertow() {
undertow.stop();
}
/**
* Sets the mode the application is running in
*
* @param providedMode A given mode or null
*/
private static void prepareMode(Mode providedMode) {
final String applicationMode = System.getProperty(Key.APPLICATION_MODE);
if (StringUtils.isNotBlank(applicationMode)) {
mode = switch (applicationMode.toLowerCase(Locale.ENGLISH)) {
case "dev" -> Mode.DEV;
case "test" -> Mode.TEST;
default -> Mode.PROD;
};
} else {
mode = providedMode;
}
}
/**
* Sets the injector wrapped through guice modules
*/
private static void prepareInjector() {
injector = Guice.createInjector(Stage.PRODUCTION, getModules());
}
/**
* Callback to MangooLifecycle applicationInitialized
*/
private static void applicationInitialized() {
getInstance(MangooBootstrap.class).applicationInitialized();
}
/**
* Checks for config failures that prevent the application from starting
*/
private static void prepareConfig() {
var config = getInstance(Config.class);
config.validate();
if (!config.isValid()) {
LOG.error("Application configuration is invalid");
failsafe();
}
int bitLength = getBitLength(config.getApplicationSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Application requires a 512 bit application secret. The current property for application.secret has currently only {} bits.", bitLength);
failsafe();
}
bitLength = getBitLength(config.getAuthenticationCookieSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Authentication cookie requires a 512 bit encryption key. The current property for authentication.cookie.secret has only {} bits.", bitLength);
failsafe();
}
bitLength = getBitLength(config.getAuthenticationCookieSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Authentication cookie requires a 512 bit sign key. The current property for authentication.cookie.signkey has only {} bits.", bitLength);
failsafe();
}
bitLength = getBitLength(config.getSessionCookieSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Session cookie secret a 512 bit encryption key. The current property for session.cookie.secret has only {} bits.", bitLength);
failsafe();
}
bitLength = getBitLength(config.getSessionCookieSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Session cookie requires a 512 bit sign key. The current property for session.cookie.signkey has only {} bits.", bitLength);
failsafe();
}
bitLength = getBitLength(config.getFlashCookieSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Flash cookie requires a 512 bit sign key. The current property for flash.cookie.signkey has only {} bits.", bitLength);
failsafe();
}
bitLength = getBitLength(config.getFlashCookieSecret());
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("Flash cookie requires a 512 bit encryption key. The current property for flash.cookie.secret has only {} bits.", bitLength);
failsafe();
}
if (!config.isDecrypted()) {
LOG.error("Found encrypted config values in config.yaml but decryption was not successful!");
failsafe();
}
if (StringUtils.isNotBlank(config.getString(Key.APPLICATION_PASETO_SECRET))) {
bitLength = getBitLength(config.getString(Key.APPLICATION_PASETO_SECRET));
if (bitLength < PASETO_SECRET_MIN_LENGTH) {
LOG.error("Paseto secret requires a 256 bit secret length. The current property for paseto.secret has only {} bits.", bitLength);
failsafe();
}
}
if (StringUtils.isNotBlank(config.getString(Key.APPLICATION_API_KEY))) {
bitLength = getBitLength(config.getString(Key.APPLICATION_API_KEY));
if (bitLength < KEY_MIN_BIT_LENGTH) {
LOG.error("API key requires a 512 bit key length. The current property for api.length has only {} bits.", bitLength);
failsafe();
}
}
}
/**
* Do sanity check on the configuration and warn about it in the log
*/
private static void sanityChecks() {
var config = getInstance(Config.class);
List warnings = new ArrayList<>();
if (!config.isAuthenticationCookieSecure()) {
var warning = "Authentication cookie has secure flag set to 'false'. It is highly recommended to set authentication.cookie.secure to 'true' in an production environment.";
warnings.add(warning);
LOG.warn(warning);
}
if (Default.AUTHENTICATION_COOKIE_NAME.equals(config.getAuthenticationCookieName())) {
var warning = "Authentication cookie name has default value. Consider changing authentication.cookie.name to an application specific value.";
warnings.add(warning);
LOG.warn(warning);
}
if (config.getAuthenticationCookieSecret().equals(config.getApplicationSecret())) {
var warning = "Authentication cookie secret is using application secret. It is highly recommended to set a dedicated value to authentication.cookie.secret.";
warnings.add(warning);
LOG.warn(warning);
}
if (!config.isSessionCookieSecure()) {
var warning = "Session cookie has secure flag set to 'false'. It is highly recommended to set session.cookie.secure to 'true' in an production environment.";
warnings.add(warning);
LOG.warn(warning);
}
if (Default.SESSION_COOKIE_NAME.equals(config.getSessionCookieName())) {
var warning = "Session cookie name has default value. Consider changing session.cookie.name to an application specific value.";
warnings.add(warning);
LOG.warn(warning);
}
if (config.getSessionCookieSecret().equals(config.getApplicationSecret())) {
var warning = "Session cookie secret is using application secret. It is highly recommended to set a dedicated value to session.cookie.secret.";
warnings.add(warning);
LOG.warn(warning);
}
if (Default.FLASH_COOKIE_NAME.equals(config.getFlashCookieName())) {
var warning = "Flash cookie name has default value. Consider changing flash.cookie.name to an application specific value.";
warnings.add(warning);
LOG.warn(warning);
}
if (config.getFlashCookieSecret().equals(config.getApplicationSecret())) {
var warning = "Flash cookie secret is using application secret. It is highly recommended to set a dedicated value to flash.cookie.secret.";
warnings.add(warning);
LOG.warn(warning);
}
getInstance(CacheProvider.class).getCache(CacheName.APPLICATION).put(Key.MANGOOIO_WARNINGS, warnings);
}
/**
* Validate if the routes that are defined in the router are valid
*/
private static void prepareRoutes() {
getInstance(MangooBootstrap.class).initializeRoutes();
Router.getRequestRoutes().forEach((RequestRoute requestRoute) -> {
if (!methodExists(requestRoute.getControllerMethod(), requestRoute.getControllerClass())) {
LOG.error("Could not find controller method '{}' in controller class '{}'", requestRoute.getControllerMethod(), requestRoute.getControllerClass());
failsafe();
}
});
}
/**
* Checks if a given method exists in a given class
* @param controllerMethod The method to check
* @param controllerClass The class to check
*
* @return True if the method exists, false otherwise
*/
private static boolean methodExists(String controllerMethod, Class> controllerClass) {
Objects.requireNonNull(controllerMethod, NotNull.CONTROLLER_METHOD);
Objects.requireNonNull(controllerClass, NotNull.CONTROLLER_CLASS);
return Arrays.stream(controllerClass.getMethods()).anyMatch(method -> method.getName().equals(controllerMethod));
}
/**
* Create routes for WebSockets ServerSentEvent and Resource files
*/
private static void createRoutes() {
pathHandler = new PathHandler(getRoutingHandler());
Router.getServerSentEventRoutes().forEach((ServerSentEventRoute serverSentEventRoute) ->
pathHandler.addExactPath(serverSentEventRoute.getUrl(),
Handlers.serverSentEvents(getInstance(ServerSentEventHandler.class)
.withAuthentication(serverSentEventRoute.hasAuthentication())))
);
Router.getPathRoutes().forEach((PathRoute pathRoute) ->
pathHandler.addPrefixPath(pathRoute.getUrl(),
new ResourceHandler(new ClassPathResourceManager(Thread.currentThread().getContextClassLoader(), Default.FILES_FOLDER + pathRoute.getUrl())))
);
pathHandler.addPrefixPath("/@admin/assets/",
new ResourceHandler(new ClassPathResourceManager(Thread.currentThread().getContextClassLoader(), "templates/@admin/assets/")));
}
private static RoutingHandler getRoutingHandler() {
var routingHandler = Handlers.routing();
routingHandler.setFallbackHandler(Application.getInstance(FallbackHandler.class));
var config = getInstance(Config.class);
if (config.isApplicationAdminEnable()) {
Bind.controller(AdminController.class)
.withRoutes(
On.get().to("/@admin").respondeWith("index"),
On.get().to("/@admin/cache").respondeWith("cache"),
On.get().to("/@admin/login").respondeWith("login"),
On.get().to("/@admin/twofactor").respondeWith("twofactor"),
On.get().to("/@admin/scheduler").respondeWith("scheduler"),
On.get().to("/@admin/tools").respondeWith("tools"),
On.get().to("/@admin/logout").respondeWith("logout"),
On.post().to("/@admin/authenticate").respondeWith("authenticate"),
On.post().to("/@admin/verify").respondeWith("verify"),
On.post().to("/@admin/tools").respondeWith("toolsRx")
);
}
Router.getRequestRoutes().forEach((RequestRoute requestRoute) -> {
var dispatcherHandler = Application.getInstance(DispatcherHandler.class)
.dispatch(requestRoute.getControllerClass(), requestRoute.getControllerMethod())
.isBlocking(requestRoute.isBlocking())
.withAuthentication(requestRoute.hasAuthentication());
routingHandler.add(requestRoute.getMethod().toString(), requestRoute.getUrl(), dispatcherHandler);
});
var resourceHandler = Handlers.resource(new ClassPathResourceManager(
Thread.currentThread().getContextClassLoader(),
Default.FILES_FOLDER + '/'));
Router.getFileRoutes().forEach((FileRoute fileRoute) -> routingHandler.add(Methods.GET, fileRoute.getUrl(), resourceHandler));
return routingHandler;
}
private static void prepareUndertow() {
var config = getInstance(Config.class);
HttpHandler httpHandler;
if (config.isMetricsEnable()) {
httpHandler = MetricsHandler.HANDLER_WRAPPER.wrap(Handlers.exceptionHandler(pathHandler)
.addExceptionHandler(Throwable.class, Application.getInstance(ExceptionHandler.class)));
} else {
httpHandler = Handlers.exceptionHandler(pathHandler)
.addExceptionHandler(Throwable.class, Application.getInstance(ExceptionHandler.class));
}
var builder = Undertow.builder()
.setServerOption(UndertowOptions.MAX_ENTITY_SIZE, config.getUndertowMaxEntitySize())
.setHandler(httpHandler);
httpHost = config.getConnectorHttpHost();
httpPort = config.getConnectorHttpPort();
ajpHost = config.getConnectorAjpHost();
ajpPort = config.getConnectorAjpPort();
var hasConnector = false;
if (httpPort > 0 && StringUtils.isNotBlank(httpHost)) {
builder.addHttpListener(httpPort, httpHost);
hasConnector = true;
}
if (ajpPort > 0 && StringUtils.isNotBlank(ajpHost)) {
builder.addAjpListener(ajpPort, ajpHost);
hasConnector = true;
}
if (hasConnector) {
undertow = builder.build();
undertow.start();
} else {
LOG.error("No connector found! Please configure a HTTP and/or an AJP connector in your config.yaml file");
failsafe();
}
}
@SuppressFBWarnings(justification = "Buffer only used locally, without user input", value = "CRLF_INJECTION_LOGS")
private static void showLogo() {
var logo = '\n' +
LOGO +
"\n\nhttps://github.com/svenkubiak/mangooio | " +
MangooUtils.getVersion() +
'\n';
LOG.info(logo);
if (httpPort > 0 && StringUtils.isNotBlank(httpHost)) {
LOG.info("HTTP connector listening @{}:{}", httpHost, httpPort);
}
if (ajpPort > 0 && StringUtils.isNotBlank(ajpHost)) {
LOG.info("AJP connector listening @{}:{}", ajpHost, ajpPort);
}
String startup = "mangoo I/O application started in " + ChronoUnit.MILLIS.between(START, LocalDateTime.now()) + " ms in " + mode + " mode. Enjoy.";
LOG.info(startup);
}
private static int getBitLength(String secret) {
Objects.requireNonNull(secret, NotNull.SECRET);
return ByteUtils.bitLength(RegExUtils.replaceAll(secret, "[^\\x00-\\x7F]", Strings.EMPTY));
}
private static List getModules() {
final List modules = new ArrayList<>();
try {
module = new io.mangoo.core.Module();
modules.add(module);
modules.add((AbstractModule) Class.forName(MODULE_CLASS).getConstructor().newInstance());
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException | ClassNotFoundException e) {
LOG.error("Failed to load modules. Check that app/Module.java exists in your application", e);
failsafe();
}
return modules;
}
private static void applicationStarted() {
getInstance(MangooBootstrap.class).applicationStarted();
}
/**
* Failsafe exit of application startup
*/
private static void failsafe() {
System.out.print("Failed to start mangoo I/O application"); //NOSONAR Intentionally as we want to exit the application at this point
System.exit(1); //NOSONAR Intentionally as we want to exit the application at this point
}
private static void prepareShutdown() {
Runtime
.getRuntime()
.addShutdownHook(getInstance(Shutdown.class));
}
private static ScanResult scanClasspath() {
return new ClassGraph()
.enableAllInfo()
.acceptPackages(ALL_PACKAGES)
.scan();
}
public static void stopEmbeddedMongoDB() {
module.stopEmbeddedMongoDB();
}
}