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

org.nuiton.jaxx.runtime.application.ApplicationBoot Maven / Gradle / Ivy

The newest version!
package org.nuiton.jaxx.runtime.application;

/*-
 * #%L
 * JAXX :: Runtime
 * %%
 * Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import io.ultreia.java4all.util.SingletonSupplier;
import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.nuiton.jaxx.runtime.application.action.ActionExecutor;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * This class is entry point of an application.
 * 

* It contains a shared instance which is automatically initialized when the first instance of class in created. *

* Nobody is allowed to modify this instance. *

* Created by tchemit on 26/01/2018. * * @author Tony Chemit - [email protected] */ public final class ApplicationBoot> { private static final Logger log = LogManager.getLogger(ApplicationBoot.class); /** * Boot scope bootLock, used to wait application end. *

* Use method {@link #lockBoot()} or {@link #unlockBoot()}. */ private static final Object bootLock = new Object(); public static String BOOT_LOG_PREFIX = ""; /** * Shared instance. */ private static ApplicationBoot INSTANCE; /** * Application initializer. */ private final ApplicationBootInitializer initializer; /** * Application config. */ private final SingletonSupplier configuration; /** * Application context. */ private final SingletonSupplier context; /** * To create new threads. */ private final SingletonSupplier threadFactory; /** * Application executor. */ private SingletonSupplier executor; /** * Internal state to know if boot was closed. */ private boolean closed; /** * Internal state to know if boot was shutdown. */ private boolean shutdown; /** * Internal state to know if boot should be reloaded at next close. */ private boolean reload; private ApplicationBoot(ApplicationBootInitializer initializer) { this.initializer = initializer; this.initializer.initOnce(); this.configuration = SingletonSupplier.of(() -> this.initializer.createConfiguration(this)); this.context = SingletonSupplier.of(() -> this.initializer.createContext(this, getConfiguration())); this.threadFactory = SingletonSupplier.of(ApplicationThreadFactory::new); this.executor = SingletonSupplier.of(() -> this.initializer.createExecutor(this, getConfiguration(), getContext())); INSTANCE = this; } /** * Method to create a new boot. * * @param initializer application initializer * @param config type * @param context type * @return new boot * @throws IllegalStateException if boot was already instantiate */ public static synchronized > ApplicationBoot create(ApplicationBootInitializer initializer) { if (INSTANCE != null) { throw new IllegalStateException("Boot is already init"); } return new ApplicationBoot<>(Objects.requireNonNull(initializer)); } private static void lockBoot() throws InterruptedException { synchronized (bootLock) { bootLock.wait(); } } public static void unlockBoot() { synchronized (bootLock) { bootLock.notifyAll(); } } public static void handlingError(String message, Exception e) { ApplicationInstances.context().handlingError(message, e); } public static void handlingError(Exception e) { ApplicationInstances.context().handlingError(e); } public static ApplicationBootInitializer initializer() { return boot().initializer; } public static C initializer(Class contextType) { return contextType.cast(initializer()); } public static ApplicationBoot boot() { return Objects.requireNonNull(INSTANCE, "boot is not init."); } public static void cleanMemory() { System.runFinalization(); System.gc(); } public static O newInstanceWithParams(Class type, Object... constructorParams) { try { return ConstructorUtils.invokeConstructor(Objects.requireNonNull(type), constructorParams); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new IllegalStateException("Can't instantiate " + type.getName(), e); } } public void startInThread() { Runtime.getRuntime().addShutdownHook(threadFactory.get().newThread(this::shutdown, "shutdown", false)); boolean shutdown = false; while (!shutdown) { Thread thread = threadFactory.get().newThread(this::start, "main", false); thread.start(); try { // lock boot lockBoot(); // boot is free to close or reload log.info(String.format("%s Application boot unlock...", BOOT_LOG_PREFIX)); } catch (InterruptedException e) { log.error(String.format("Main thread was interrupted for reason %s", e.getMessage()), e); } finally { if (reload) { try { close(); } catch (Exception e) { log.error(String.format("Could not close boot, will shutdown application: %s", e.getMessage()), e); // if can't close, then shutdown shutdown = true; } } else { // if no reload, this means shutdown shutdown = true; } } } shutdown(); } public void start() { // when starting, we are no more closing, nor reloading reload = closed = closing = false; try { initializer.init(getConfiguration(), getContext()); } catch (ApplicationBootInitializerException e) { log.error("Could not init boot", e); shutdown(); } if (shutdown) { return; } try { initializer.start(getConfiguration(), getContext()); } catch (Exception e) { log.error("Could not start boot", e); shutdown(); } } public void restart() { reload = true; unlockBoot(); } public void shutdown() { if (shutdown) { return; } shutdown = true; log.info(String.format("%s Ask to shutdown application at %s", BOOT_LOG_PREFIX, new Date())); int exitCode = 0; try { close(); } catch (Exception e) { exitCode = 1; log.error(String.format("Can't shutdown properly application: %s", e.getMessage()), e); } finally { try { initializer.close(); } finally { INSTANCE = null; exit(exitCode); } } } public void addAction(String actionLabel, Runnable action) { getExecutor().addAction(actionLabel, action); } public ApplicationThreadFactory getThreadFactory() { checkNotClosed("threadFactory"); return threadFactory.get(); } public Context getContext() { checkNotClosed("context"); return context.get(); } public Config getConfiguration() { checkNotClosed("configuration"); return configuration.get(); } public boolean isClosed() { return closed; } public void checkNotClosed(String stateName) { if (closed) { throw new IllegalStateException(String.format("Can't get access boot internal state: %s, boot is closing (or closed).", stateName)); } } @Override protected final void finalize() throws Throwable { if (!shutdown) { shutdown(); } super.finalize(); } private boolean closing; public boolean isClosing() { return closing; } public void close() { if (closed || closing) { return; } closing = true; try { if (context.withValue()) { context.get().close(); } if (executor.withValue()) { executor.get().close(); } } catch (Exception e) { throw new RuntimeException("Can't close boot for reason: " + e.getMessage(), e); } finally { cleanMemory(); configuration.clear(); context.clear(); executor.clear(); closed = true; } } private void exit(int code) { log.debug(String.format("%s Ask to exit application with code: %s", BOOT_LOG_PREFIX, code)); if (initializer.haltOnExit()) { Runtime.getRuntime().halt(code); } log.info(String.format("%s Application exit at %s", BOOT_LOG_PREFIX, new Date())); } private ActionExecutor getExecutor() { checkNotClosed("executor"); return executor.get(); } public ThreadPoolExecutor getThreadPoolExecutor() { return getExecutor().getWorkersExecutorService(); } public static class ApplicationThreadFactory implements ThreadFactory { private final ThreadFactory defaultFactory; private ApplicationThreadFactory() { defaultFactory = Executors.defaultThreadFactory(); } @Override public Thread newThread(@SuppressWarnings("NullableProblems") Runnable r) { Thread thread = defaultFactory.newThread(Objects.requireNonNull(r)); thread.setName("Application." + thread.getName()); thread.setDaemon(true); return thread; } public Thread newThread(Runnable r, String name, boolean daemon) { Thread thread = newThread(Objects.requireNonNull(r)); thread.setName("Application." + Objects.requireNonNull(name)); thread.setDaemon(daemon); return thread; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy