x-control-panel/app/src/main/java/ru/kirillius/XCP/Application.java

170 lines
6.1 KiB
Java

package ru.kirillius.XCP;
import lombok.Getter;
import ru.kirillius.XCP.Commons.Config;
import ru.kirillius.XCP.Commons.ConfigManager;
import ru.kirillius.XCP.Commons.Context;
import ru.kirillius.XCP.Commons.Service;
import ru.kirillius.XCP.Logging.Logger;
import ru.kirillius.XCP.Logging.LoggingSystem;
import ru.kirillius.XCP.Logging.LoggingSystemImpl;
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
import ru.kirillius.XCP.Security.ConfigManagerImpl;
import ru.kirillius.XCP.Security.SecurityManager;
import ru.kirillius.XCP.Security.SecurityManagerImpl;
import ru.kirillius.XCP.Services.ServiceLoadPriority;
import ru.kirillius.XCP.Services.WebService;
import ru.kirillius.XCP.web.WebServiceImpl;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Getter
public class Application implements Context {
@Getter
private final SecurityManager securityManager;
@Getter
private final LoggingSystem loggingSystem;
private final Logger log;
@Getter
private final List<String> launchArgs;
@Getter
private final Config config;
@Getter
private final ConfigManager configManager;
private final Map<Class<? extends Service>, Service> services = new ConcurrentHashMap<>();
private Config loadConfig() {
var configFile = configManager.getConfigFile().getAbsolutePath();
try {
configFile = configManager.getConfigFile().getCanonicalPath();
} catch (IOException e) {
log.warning("Unable to determine real path of file " + configFile);
}
Config config;
if (configManager.isExist()) {
try {
config = configManager.load();
log.info("Loaded config file: " + configFile);
} catch (IOException e) {
log.error("Error loading config file " + configFile, e);
throw new RuntimeException(e);
}
} else {
log.warning("Unable to find config file " + configFile + ". Using default values.");
config = configManager.create();
log.info("Saving default config file to " + configFile);
try {
configManager.save(config);
} catch (IOException e) {
throw new RuntimeException("Unable to save config file", e);
}
}
return config;
}
public Application(String[] args) {
launchArgs = Arrays.stream(args).toList();
loggingSystem = new LoggingSystemImpl(this);
log = loggingSystem.createLogger(Application.class);
configManager = new ConfigManagerImpl(this);
config = loadConfig();
securityManager = new SecurityManagerImpl();
try {
loadServices();
} catch (Throwable throwable) {
log.error(throwable);
shutdown();
throw new RuntimeException("Error loading services");
}
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
((WebServiceImpl) getService(WebService.class)).join();
}
private void loadServices() {
var servicesToLoad = List.of(RepositoryServiceImpl.class, WebServiceImpl.class);
servicesToLoad.stream().sorted(Comparator.comparingInt(aClass -> {
var order = aClass.getAnnotation(ServiceLoadPriority.class);
return order == null ? 100000 : order.value();
})).forEach(aClass -> {
@SuppressWarnings("unchecked") var facade = (Class<? extends Service>) Arrays.stream(aClass.getInterfaces())
.filter(Service.class::isAssignableFrom)
.findFirst().
orElseThrow(() -> new ClassCastException("Unable to get service interface from class " + aClass.getSimpleName()));
log.info("Loading service " + facade.getSimpleName());
try {
var constructor = aClass.getConstructor();
try {
var service = constructor.newInstance();
try {
service.initialize(this);
services.put(facade, service);
} catch (Throwable e) {
try {
service.close();
} catch (IOException ex) {
e.addSuppressed(ex);
}
throw new RuntimeException("Failed to start " + facade.getSimpleName(), e);
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to instantiate " + facade.getSimpleName(), e);
}
} catch (NoSuchMethodException e) {
throw new RuntimeException("Failed to find default constructor of " + facade.getSimpleName(), e);
}
});
}
@SuppressWarnings("unchecked")
@Override
public <S extends Service> S getService(Class<S> serviceClass) {
return (S) services.get(serviceClass);
}
@Override
public void shutdown() {
try {
services.forEach((serviceClass, service) -> {
try {
log.info("Shutting down service " + serviceClass.getSimpleName());
service.close();
} catch (IOException e) {
log.error("Error shutting down service " + serviceClass.getSimpleName(), e);
}
});
loggingSystem.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isDebuggingEnabled() {
return launchArgs.contains("--debug");
}
public static void main(String[] args) {
try {
new Application(args);
} catch (Throwable e) {
System.err.println("Error starting application");
e.printStackTrace(System.err);
System.exit(1);
}
}
}