170 lines
6.1 KiB
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);
|
|
}
|
|
}
|
|
|
|
}
|