Erweiterungen
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>de.assecutor.emulatorstation</groupId>
|
||||
<artifactId>emulatorstation</artifactId>
|
||||
<version>0.9.12</version>
|
||||
<version>0.9.13</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
||||
@@ -10,8 +10,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import de.assecutor.emulatorstation.pojo.UserInfo;
|
||||
import de.assecutor.emulatorstation.pojo.NiederlassungInfo;
|
||||
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@Push
|
||||
@EnableScheduling
|
||||
|
||||
@Theme("default")
|
||||
public class Application implements AppShellConfigurator {
|
||||
// Single user configuration
|
||||
@@ -31,6 +35,7 @@ public class Application implements AppShellConfigurator {
|
||||
Map.entry("Frankfurt am Main", new NiederlassungInfo("Frankfurt am Main", "172.18.0.105", "6085", "/frankfurt")),
|
||||
Map.entry("Geschäftführung", new NiederlassungInfo("Geschäftführung", "172.18.0.112", "6092", "/gfl")));
|
||||
|
||||
public static final java.util.Map<String, com.vaadin.flow.server.WrappedSession> sessionsById = new ConcurrentHashMap<>();
|
||||
public static final Map<String, String> activeNiederlassungen = new ConcurrentHashMap<>();
|
||||
public static final Map<String, String> activeSessions = new ConcurrentHashMap<>(); // sessionId -> niederlassung
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package de.assecutor.emulatorstation.base.domain;
|
||||
|
||||
import de.assecutor.emulatorstation.Application;
|
||||
import de.assecutor.emulatorstation.pojo.NiederlassungInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
@Service
|
||||
public class ContainerShutdownScheduler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ContainerShutdownScheduler.class);
|
||||
|
||||
private static final String DOCKER_HOST = "172.16.0.158"; // gleiches Fallback wie in MainView/LoginView
|
||||
|
||||
private final HttpClient http = HttpClient.newHttpClient();
|
||||
|
||||
// Täglich um 22:00 Uhr Serverzeit
|
||||
@Scheduled(cron = "0 0 22 * * *")
|
||||
public void shutdownAllRunningContainers() {
|
||||
logger.info("[Scheduler] Starte nächtliches Herunterfahren aller laufenden Emulator-Container (22:00)");
|
||||
for (NiederlassungInfo info : Application.niederlassungen.values()) {
|
||||
String containerName = buildContainerName(info);
|
||||
try {
|
||||
if (isRunning(containerName)) {
|
||||
stopContainer(containerName);
|
||||
} else {
|
||||
logger.info("[Scheduler] Container '{}' läuft nicht – nichts zu tun", containerName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("[Scheduler] Fehler beim Beenden des Containers '{}': {}", containerName, e.toString());
|
||||
}
|
||||
}
|
||||
logger.info("[Scheduler] Nächtliches Herunterfahren abgeschlossen");
|
||||
|
||||
// Alle Sessions beenden (Logout erzwingen)
|
||||
logger.info("[Scheduler] Beginne Session-Logout für alle aktiven Sessions: {}", Application.sessionsById.size());
|
||||
for (com.vaadin.flow.server.WrappedSession session : new java.util.ArrayList<>(Application.sessionsById.values())) {
|
||||
try {
|
||||
session.invalidate();
|
||||
} catch (Exception e) {
|
||||
logger.warn("[Scheduler] Fehler beim Invalidieren einer Session: {}", e.toString());
|
||||
}
|
||||
}
|
||||
// Zur Sicherheit interne Mappings leeren
|
||||
Application.activeSessions.clear();
|
||||
Application.activeNiederlassungen.clear();
|
||||
Application.sessionsById.clear();
|
||||
logger.info("[Scheduler] Session-Logout abgeschlossen. Aktive Sessions: {} / Maps geleert.", Application.activeSessions.size());
|
||||
|
||||
}
|
||||
|
||||
private String buildContainerName(NiederlassungInfo info) {
|
||||
return "android-container-" + info.name();
|
||||
}
|
||||
|
||||
private boolean isRunning(String containerName) throws Exception {
|
||||
HttpRequest req = HttpRequest.newBuilder()
|
||||
.uri(URI.create("http://" + DOCKER_HOST + ":2375/containers/" + containerName + "/json"))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
|
||||
if (resp.statusCode() == 200) {
|
||||
String body = resp.body();
|
||||
boolean running = body != null && body.contains("\"Running\":true");
|
||||
logger.info("[Scheduler] Inspect '{}' -> running={}", containerName, running);
|
||||
return running;
|
||||
} else if (resp.statusCode() == 404) {
|
||||
logger.info("[Scheduler] Container '{}' existiert nicht (404)", containerName);
|
||||
return false;
|
||||
} else {
|
||||
logger.info("[Scheduler] Inspect '{}' -> status {}", containerName, resp.statusCode());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopContainer(String containerName) throws Exception {
|
||||
HttpRequest req = HttpRequest.newBuilder()
|
||||
.uri(URI.create("http://" + DOCKER_HOST + ":2375/containers/" + containerName + "/stop"))
|
||||
.POST(HttpRequest.BodyPublishers.noBody())
|
||||
.build();
|
||||
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
|
||||
int status = resp.statusCode();
|
||||
if (status == 204 || status == 304) {
|
||||
logger.info("[Scheduler] Container '{}' gestoppt (status={})", containerName, status);
|
||||
} else if (status == 404) {
|
||||
logger.info("[Scheduler] Container '{}' nicht gefunden (404)", containerName);
|
||||
} else {
|
||||
logger.warn("[Scheduler] Unerwarteter Status beim Stoppen von '{}' -> {} / {}", containerName, status, resp.body());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,12 +99,6 @@ public class LoginView extends VerticalLayout {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prüfen ob Niederlassung bereits belegt ist
|
||||
if (Application.activeNiederlassungen.containsKey(niederlassung)) {
|
||||
Notification.show("Niederlassung ist bereits von einer anderen Session belegt", 3000,
|
||||
Notification.Position.MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
var niederlassungInfo = Application.niederlassungen.get(niederlassung);
|
||||
if (niederlassungInfo == null) {
|
||||
@@ -154,4 +148,79 @@ public class LoginView extends VerticalLayout {
|
||||
MainLayout.instance.setDrawerOpened(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isContainerRunning(de.assecutor.emulatorstation.pojo.NiederlassungInfo info) {
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
final String srv = "172.16.0.158"; // Fallback wie in MainView
|
||||
final String name = "android-container-" + info.name();
|
||||
try {
|
||||
var client = java.net.http.HttpClient.newHttpClient();
|
||||
var request = java.net.http.HttpRequest.newBuilder()
|
||||
.uri(java.net.URI.create("http://" + srv + ":2375/containers/" + name + "/json"))
|
||||
.GET()
|
||||
.build();
|
||||
var response = client.send(request, java.net.http.HttpResponse.BodyHandlers.ofString());
|
||||
if (response.statusCode() == 200) {
|
||||
var body = response.body();
|
||||
return body != null && body.contains("\"Running\":true");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Fehler beim Pruefen des Container-Status fuer '{}': {}", name, e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void completeLogin(com.vaadin.flow.component.UI ui, String password,
|
||||
de.assecutor.emulatorstation.pojo.NiederlassungInfo niederlassungInfo,
|
||||
boolean adoptRunningContainer) {
|
||||
String niederlassung = niederlassungInfo.name();
|
||||
|
||||
String sessionId = ui.getSession().getSession().getId();
|
||||
// Session niemals ablaufen lassen
|
||||
ui.getSession().getSession().setMaxInactiveInterval(-1);
|
||||
|
||||
// HttpSession/WrappedSession im Registry merken, um später invalidieren zu können
|
||||
Application.sessionsById.put(sessionId, ui.getSession().getSession());
|
||||
|
||||
|
||||
// Session registrieren
|
||||
Application.activeSessions.put(sessionId, niederlassung);
|
||||
Application.activeNiederlassungen.put(niederlassung, sessionId);
|
||||
|
||||
// Log that Niederlassung is now blocked
|
||||
logger.info("Niederlassung '{}' wurde gesperrt fuer Session {}", niederlassung, sessionId);
|
||||
logger.info("Aktive Niederlassungen: {}", Application.activeNiederlassungen.keySet());
|
||||
|
||||
// Spring Security Authentifizierung setzen
|
||||
var authorities = java.util.List.of(new org.springframework.security.core.authority.SimpleGrantedAuthority("ROLE_USER"));
|
||||
var authentication = new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(
|
||||
Application.SINGLE_USERNAME, password, authorities);
|
||||
org.springframework.security.core.context.SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
// Spring Security Kontext explizit in der HTTP-Session speichern, damit Reload eingeloggt bleibt
|
||||
ui.getSession().getSession().setAttribute(
|
||||
org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
|
||||
org.springframework.security.core.context.SecurityContextHolder.getContext());
|
||||
|
||||
// Session-Daten setzen
|
||||
ui.getSession().setAttribute("user", Application.SINGLE_USERNAME);
|
||||
ui.getSession().setAttribute("username", Application.SINGLE_USERNAME);
|
||||
ui.getSession().setAttribute("niederlassung", niederlassungInfo);
|
||||
ui.getSession().setAttribute("sessionId", sessionId);
|
||||
if (adoptRunningContainer) {
|
||||
// UI soll direkt in gestarteten Zustand gehen
|
||||
ui.getSession().setAttribute("emulatorStarted", true);
|
||||
}
|
||||
|
||||
logger.info("Login erfolgreich - Session-Daten gesetzt:");
|
||||
logger.info("Username: {}", Application.SINGLE_USERNAME);
|
||||
logger.info("Niederlassung: {}", niederlassungInfo.name());
|
||||
logger.info("SessionId: {}", sessionId);
|
||||
logger.info("Aktive Sessions: {}/{}", Application.activeSessions.size(), Application.MAX_ACTIVE_SESSIONS);
|
||||
|
||||
ui.navigate("main");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -740,6 +740,8 @@ public final class MainView extends Main implements BeforeEnterObserver {
|
||||
if (sessionId != null) {
|
||||
Application.activeSessions.remove(sessionId);
|
||||
logger.info("Session {} aus aktiven Sessions entfernt", sessionId);
|
||||
Application.sessionsById.remove(sessionId);
|
||||
|
||||
}
|
||||
if (niederlassung != null) {
|
||||
Application.activeNiederlassungen.remove(niederlassung.name());
|
||||
|
||||
@@ -13,9 +13,14 @@ public class SessionListener implements SessionDestroyListener {
|
||||
public void sessionDestroy(SessionDestroyEvent event) {
|
||||
String username = (String) event.getSession().getAttribute("user");
|
||||
NiederlassungInfo niederlassung = (NiederlassungInfo) event.getSession().getAttribute("niederlassung");
|
||||
String sessionId = (String) event.getSession().getAttribute("sessionId");
|
||||
|
||||
if (username != null && niederlassung != null) {
|
||||
Application.activeNiederlassungen.remove(niederlassung.name());
|
||||
}
|
||||
if (sessionId != null) {
|
||||
Application.activeSessions.remove(sessionId);
|
||||
Application.sessionsById.remove(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user