package org.openslx.bwlp.sat;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransportException;
import org.openslx.bwlp.sat.database.Updater;
import org.openslx.bwlp.sat.database.mappers.DbConfiguration;
import org.openslx.bwlp.sat.database.mappers.DbUser;
import org.openslx.bwlp.sat.fileserv.FileServer;
import org.openslx.bwlp.sat.maintenance.DeleteOldImages;
import org.openslx.bwlp.sat.maintenance.DeleteOldLectures;
import org.openslx.bwlp.sat.maintenance.DeleteOldUsers;
import org.openslx.bwlp.sat.maintenance.MailFlusher;
import org.openslx.bwlp.sat.maintenance.SendExpireWarning;
import org.openslx.bwlp.sat.thrift.BinaryListener;
import org.openslx.bwlp.sat.thrift.JsonHttpListener;
import org.openslx.bwlp.sat.thrift.cache.OperatingSystemList;
import org.openslx.bwlp.sat.thrift.cache.OrganizationList;
import org.openslx.bwlp.sat.thrift.cache.VirtualizerList;
import org.openslx.bwlp.sat.util.Configuration;
import org.openslx.bwlp.sat.util.Identity;
import org.openslx.bwlp.sat.web.WebServer;
import org.openslx.bwlp.thrift.iface.TInvalidTokenException;
import org.openslx.sat.thrift.version.Version;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.thrifthelper.ThriftManager.ErrorCallback;
import org.openslx.util.AppUtil;
import org.openslx.util.GrowingThreadPoolExecutor;
import org.openslx.util.PrioThreadFactory;
import org.openslx.util.QuickTimer;
public class App {
private static Logger LOGGER = LogManager.getLogger(App.class);
private static final String NAME = "bwLehrpool-Server";
private static final Set<String> failFastMethods = new HashSet<>();
public static void main(String[] args)
throws TTransportException, NoSuchAlgorithmException, IOException, KeyManagementException {
System.setProperty("mariadb.logging.disable", "true");
// setup basic logging appender to log output on console if no external appender (log4j.properties) is configured
if (org.apache.logging.log4j.core.Logger.class.cast(LogManager.getRootLogger())
.getAppenders()
.isEmpty()) {
Configurator.initialize(new DefaultConfiguration());
}
// get Configuration
try {
LOGGER.info("Loading configuration");
Configuration.load();
} catch (Exception e1) {
LOGGER.fatal("Could not load configuration", e1);
System.exit(1);
}
AppUtil.logHeader(LOGGER, App.NAME, App.class.getPackage().getImplementationVersion());
AppUtil.logProperty(LOGGER, "rpc.version", Long.toString(Version.VERSION));
AppUtil.logProperty(LOGGER, "server.features", SupportedFeatures.getFeatureString());
// Update database schema if applicable
try {
Updater.updateDatabase();
RuntimeConfig.get();
DbConfiguration.updateMailTemplates(false);
} catch (SQLException e1) {
LOGGER.fatal("Updating/checking the database layout failed.");
return;
}
if (Identity.loadCertificate() == null) {
LOGGER.error("Could not set up TLS/SSL requirements, exiting");
System.exit(1);
}
failFastMethods.add("getVirtualizers");
failFastMethods.add("getOperatingSystems");
failFastMethods.add("getOrganizations");
ThriftManager.setMasterErrorCallback(new ErrorCallback() {
@Override
public boolean thriftError(int failCount, String method, Throwable t) {
if (failFastMethods.contains(method))
return false;
if (failCount > 2 || t == null) {
LOGGER.warn("Thrift Client error for " + method + ", FAIL.");
return false;
}
if (t instanceof TInvalidTokenException)
return false;
if (((TException) t).getCause() == null) {
LOGGER.info("Thrift error " + t.toString() + " for " + method + ", retrying...");
} else {
LOGGER.info("Thrift error " + ((TException) t).getCause().toString() + " for " + method
+ ", retrying...");
}
try {
Thread.sleep(failCount * 250);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return true;
}
});
SSLContext ctx = null;
if (Configuration.getMasterServerSsl()) {
ctx = Configuration.getMasterServerSslContext();
}
ThriftManager.setMasterServerAddress(ctx, Configuration.getMasterServerAddress(),
Configuration.getMasterServerPort(), 30000);
// Load useful things from master server
if (VirtualizerList.get() == null || VirtualizerList.get().isEmpty()) {
LOGGER.fatal("Could not get initial virtualizer list from master server."
+ " Please make sure this server can connect to the internet.");
return;
}
if (OrganizationList.get() == null || OrganizationList.get().isEmpty()) {
LOGGER.fatal("Could not get initial organization list from master server."
+ " Please make sure this server can connect to the internet.");
return;
}
if (OperatingSystemList.get() == null || OperatingSystemList.get().isEmpty()) {
LOGGER.fatal("Could not get initial operating system list from master server."
+ " Please make sure this server can connect to the internet.");
return;
}
// Start file transfer server
if (!FileServer.instance().start()) {
LOGGER.error("Could not start internal file server.");
return;
}
// Start watch dog to ensure nobody else is messing with the vmstore
QuickTimer.scheduleAtFixedDelay(new StorageUseCheck(), 10000, 60000);
// Set a flag that we need to convert userids if applicable
DbUser.checkIfLegacyUsersExist();
// Set up maintenance tasks
DeleteOldImages.init();
SendExpireWarning.init();
MailFlusher.init();
DeleteOldLectures.init();
DeleteOldUsers.init();
// Shared executor for SSL thrift and HTTP thrift
ExecutorService es = new GrowingThreadPoolExecutor(3, 128, 2, TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(4), new PrioThreadFactory("Web/Thrift"));
// Start Thrift Server
Thread t;
// Plain
t = new Thread(new BinaryListener(9090, false, es), "Thr-Plain:9090");
t.setDaemon(true);
t.start();
// SSL
t = new Thread(new BinaryListener(9091, true, es), "Thr-SSL:9091");
t.start();
// Start RPC httpd
t = new Thread(new WebServer(9080), "RPC-httpd:9080");
t.setDaemon(true);
t.start();
// Start JSON-Thrift httpd
t = new Thread(new JsonHttpListener(9081, es), "Thr-httpd:9081");
t.setDaemon(true);
t.start();
Runtime.getRuntime().addShutdownHook(new Thread("Shutdown") {
@Override
public void run() {
QuickTimer.cancel();
LOGGER.info("All services and workers shut down, exiting...");
}
});
}
}