Warum ein Aktiv-/Inaktiv-Modell für Shortlinks?
Für viele Benutzer – insbesondere solche, die im Bereich der Softwareentwicklung arbeiten – sind Shortlinks weit mehr als nur einfache URL-Verkürzungen. Sie fungieren als flexible Weiterleitungsmechanismen für Kampagnen, Feature-Steuerungen, Testszenarien sowie interne Tools. Entsprechend hoch sind die Anforderungen an Transparenz, Steuerbarkeit und ein sauberes Lifecycle-Management.
Der URL-Shortener erhält dafür ein Aktiv-/Inaktiv-Modell, das diese Ansprüche direkt unterstützt. Ein Benutzer kann nun für jeden Shortlink festlegen, ob dieser aktiv sein soll, temporär deaktiviert wird oder aufgrund eines geplanten Ablaufs keine Nutzer mehr erreichen darf. Gleichzeitig wird deutlich zwischen einem deaktivierten und einem abgelaufenen Shortlink unterschieden – sowohl in der UI als auch im http-Verhalten.
Für den Benutzer entsteht dadurch ein spürbarer Zugewinn an Kontrolle. Eine gesamte Kampagne kann mit einem Klick offline genommen werden, ohne Daten zu verlieren und ohne mehrere Einträge einzeln bearbeiten zu müssen. Über die REST-API oder den Java-Client lässt sich der Aktivitätsstatus zudem automatisiert von außen steuern, wodurch auch Integrationen in bestehende Entwicklungs-‑ und Deployment-Prozesse möglich werden.
Diese Einleitung skizziert die Motivation für das neue Modell. Die folgenden Kapitel zeigen im Detail, wie UI, API, Datenmodell und Redirect-Logik erweitert wurden und wie Benutzer vom neuen Aktiv-/Inaktiv-Mechanismus profitieren.
Der Quelltext zu diesem Projektstand befindet sich auf GitHub unter folgender URL: https://github.com/svenruppert/url-shortener/tree/feature/advent-2025-day-10


Architektur-Überblick der Änderungen
Das neue Aktiv/Inaktiv-Modell wirkt nicht isoliert innerhalb eines einzelnen Moduls, sondern erstreckt sich über mehrere Schichten der Anwendung. Für den Benutzer entsteht dadurch ein konsistentes Verhalten – egal ob ein Shortlink über die UI bearbeitet, über die API automatisiert verwaltet oder direkt im Browser aufgerufen wird. Um dieses Zusammenspiel zu verstehen, lohnt sich ein Blick in die zentralen Bereiche, in denen Anpassungen vorgenommen wurden.
Die Grundlage bildet das erweiterte Datenmodell, das nun ein zusätzliches Attribut zur Speicherung des Aktivitätsstatus enthält. Dieses Merkmal bildet den gemeinsamen Nenner für sämtliche Operationen rund um Shortlinks. Darauf aufbauend wurden sowohl die REST-Endpunkte als auch der interne Java-Client erweitert, sodass der Aktivitätsstatus beim Anlegen, Ändern oder Abfragen eines Shortlinks zuverlässig übermittelt wird.
In der Benutzeroberfläche spiegeln sich diese strukturellen Änderungen ebenfalls wider. Sowohl beim Erstellen eines Shortlinks als auch in der Übersichtstabelle oder im Detaildialog kann der Aktivitätsstatus nun direkt gesteuert werden. Aktionen wie das Aktivieren oder Deaktivieren mehrerer Einträge gleichzeitig greifen dabei auf dieselben Mechanismen zurück wie die API, wodurch ein einheitliches Anwendungserlebnis entsteht.
Nicht zuletzt wurde auch das Redirect-Verhalten angepasst. Während abgelaufene Shortlinks einen eindeutigen Hinweis auf ihren Status erhalten, lassen sich deaktivierte Shortlinks klar unterscheiden. Auf diese Weise kann der Benutzer nicht nur selbst besser nachvollziehen, weshalb ein Shortlink nicht erreichbar ist, sondern erhält auch aus Client- und Monitoring-Sicht präzisere Rückmeldungen.
Dieser architektonische Überblick zeigt, dass das Aktiv/Inaktiv-Modell kein punktuelles Feature ist, sondern ein konsistentes, durchgängiges Designprinzip über alle Schichten des Systems hinweg. In den folgenden Kapiteln werden die jeweiligen Bereiche im Detail betrachtet und technisch erläutert.
Das erweiterte Datenmodell
Bevor das Aktiv-/Inaktiv-Modell in der Benutzeroberfläche, in der REST-API oder im Redirect-Verhalten sichtbar wird, muss die grundlegende Struktur im Datenmodell vorgesehen sein. Kapitel 3 widmet sich genau dieser Basis und zeigt, wie der Aktivitätsstatus eines Shortlinks im Kern des Systems verankert ist.
Das Datenmodell ist der zentrale Ort, an dem alle relevanten Informationen zu einem Shortlink zusammenlaufen. Jede Benutzeraktion – sei es das Erstellen, Bearbeiten, Filtern oder Weiterleiten eines Shortlinks – greift früher oder später auf diese Struktur zurück. Daher ist es entscheidend, den Aktivitätsstatus dort einzuführen, wo er konsistent und zuverlässig verfügbar ist.
In diesem Kapitel wird gezeigt, wie das neue active-Attribut in den Domänenobjekten abgebildet wird, wie es in den DTOs transportiert wird und welche Überlegungen hinsichtlich der Serialisierung und der Abwärtskompatibilität eine Rolle spielen. Durch diese saubere Verankerung im Datenmodell wird das Aktiv/Inaktiv-Modell zu einem stabilen Bestandteil der gesamten Anwendung, auf dem alle weiteren technischen Erweiterungen aufbauen.
Neues active-Flag im Core-Domain-Modell.
Die Einführung des Aktiv/Inaktiv-Modells beginnt auf der Ebene des Core-Domain-Modells. Hier wird festgelegt, wie ein Shortlink seinen Status speichert und welche Informationen innerhalb der Anwendung übertragen werden. Das neue Attribut active bildet die Grundlage dafür, dass der Benutzer jederzeit nachvollziehen kann, ob ein Shortlink derzeit genutzt wird oder bewusst deaktiviert ist.
Das zentrale Element dieser Änderung ist die Erweiterung der Klasse ShortUrlMapping. Sie beschreibt die wesentlichen Eigenschaften eines Shortlinks – von seinem Alias über die ursprüngliche Ziel-URL bis hin zu den Ablaufdaten. Durch das neue Attribut wird diese Struktur nun um die Möglichkeit ergänzt, den Aktivitätsstatus eindeutig zu bestimmen.
In der Originalimplementierung sieht der relevante Ausschnitt folgendermaßen aus:
// Auszug aus ShortUrlMapping
private boolean active;
public boolean active() {
return active;
}
public ShortUrlMapping withActive(boolean active) {
return new ShortUrlMapping(
this.shortCode,
this.originalUrl,
this.createdAt,
this.expiresAt,
active
);
}
Diese Erweiterung sorgt dafür, dass der Aktivitätsstatus sowohl im Speicher als auch in allen nachgelagerten Schichten des Systems zuverlässig zur Verfügung steht. Das Modell bleibt dabei unveränderlich. Der Status wird nicht nachträglich in einem bestehenden Objekt verändert, sondern über die Methode withActive als neue Instanz erzeugt. Dies verhindert unbeabsichtigte Seiteneffekte und unterstützt ein konsistentes, deterministisches Datenverhalten.
Mit dieser klaren Trennung zwischen aktivem und inaktivem Zustand entsteht eine stabile Grundlage, auf der UI, Server und Redirect-Logik aufbauen können. In den folgenden Unterkapiteln wird darauf eingegangen, wie diese Information in den DTOs abgebildet wird und welche Konsequenzen sich daraus für das Serialisierungs- und Transportverhalten ergeben.
Auswirkungen auf DTOs (ShortenRequest, ShortUrlMapping)
Das Aktiv/Inaktiv-Modell entfaltet seine Wirkung nicht nur im Domänenkern, sondern muss auch über die Grenze der Anwendung hinweg sauber transportiert werden. Dafür sind die Data Transfer Objects (DTOs) entscheidend. Sie bilden die Schnittstelle zwischen Benutzeroberfläche, REST-API und Java-Client und stellen sicher, dass der Aktivitätsstatus eines Shortlinks korrekt übertragen wird.
Mit der Erweiterung des Datenmodells erhält auch das DTO ShortUrlMapping ein zusätzliches Attribut, das den Status eines Shortlinks widerspiegelt. Dieser Wert wird sowohl in der Kommunikation vom Server zur UI als auch zwischen Server und Client verwendet. Ein relevanter Auszug aus der originalen Implementierung sieht wie folgt aus:
// Auszug aus ShortUrlMapping DTO
public record ShortUrlMapping(
String shortCode,
String originalUrl,
Instant createdAt,
Instant expiresAt,
boolean active
) {}
Dieses erweiterte DTO bildet die Grundlage dafür, dass der Benutzer den Aktivitätsstatus in der UI sehen kann oder dass externe Systeme den Status auslesen und darauf reagieren können. Durch die Verwendung eines Records bleiben Struktur und Serialisierung schlank und klar definiert.
Auch der Request zum Anlegen oder Ändern eines Shortlinks wurde angepasst. Der ShortenRequest transportiert nun ebenfalls die Information, ob ein Shortlink direkt aktiv oder zunächst deaktiviert erzeugt werden soll. Dies ermöglicht dem Benutzer, bereits im Erstellungsprozess gezielt zu steuern, wie sich der Shortlink verhalten soll.
Ein Auszug aus dem entsprechenden Record:
// Auszug aus ShortenRequest
public record ShortenRequest(
String shortCode,
String originalUrl,
Instant expiresAt,
Boolean active
) {}
An dieser Stelle fällt auf, dass der Request ein Boolean verwendet, während das Mapping selbst ein primitives Boolean besitzt. Dieser Unterschied ist bewusst gewählt: Der Wert im Request kann auch null sein, wenn der Benutzer keinen expliziten Zustand setzen möchte. In diesem Fall entscheidet die Serverlogik über einen Standardwert. Das Mapping hingegen besitzt stets einen definierten Zustand, sodass keine Mehrdeutigkeit entsteht.
Durch die Erweiterung der DTOs wird gewährleistet, dass das Aktiv/Inaktiv-Modell über alle Systemgrenzen hinweg einheitlich verfügbar ist. Es bildet die Grundlage dafür, dass UI, API und Client identisch über den Status eines Shortlinks informiert werden und so ein konsistentes Benutzererlebnis entsteht.
Serialisierung und Abwärtskompatibilität
Damit das Aktiv/Inaktiv-Modell zuverlässig funktioniert, muss der neue active-Wert nicht nur im internen Datenmodell vorhanden sein, sondern auch korrekt serialisiert und über verschiedene Komponenten hinweg transportiert werden. Die Erweiterung der Serialisierung betrifft insbesondere zwei Bereiche: die JSON-Kommunikation über REST und den Datenaustausch zwischen den verschiedenen Anwendungsmodulen.
Durch die Verwendung von Java Records in den DTOs wird die Serialisierung weitgehend von der gewählten JSON-Bibliothek übernommen. Sobald das active-Attribut in den Konstruktoren der Records definiert ist, wird es automatisch in die JSON-Repräsentation einbezogen. Dies macht die Integration unkompliziert und minimiert den Bedarf an zusätzlicher Konfiguration.
Ein Beispiel für die resultierende JSON-Struktur eines Shortlinks, wie sie über die REST-API ausgeliefert wird:
{
"shortCode": "abc123",
"originalUrl": "https://example.com",
"createdAt": "2025-05-20T10:15:30Z",
"expiresAt": "2025-06-01T00:00:00Z",
"active": true
}
Die JSON-Struktur macht deutlich, dass der Aktivitätsstatus für den Benutzer sofort sichtbar und ohne zusätzliche Logik abrufbar ist. Genau dieser Transparenzgewinn ist zentral für das neue Modell.
Ein wichtiger Aspekt dieser Änderung ist die Abwärtskompatibilität. Systeme oder Komponenten, die ältere Versionen der API nutzen und das active-Feld nicht kennen, können weiterhin problemlos interagieren. JSON-Verbraucher ignorieren üblicherweise unbekannte Felder, sodass ältere Clients nicht von zusätzlichen Informationen beeinträchtigt werden. Gleichzeitig kann der Server für Requests, die keinen active-Wert angeben, einen sinnvollen Standardwert setzen – typischerweise true.
Diese Kombination aus klarer Serialisierung und hoher Abwärtskompatibilität stellt sicher, dass das Aktiv/Inaktiv-Modell ohne Bruch in bestehende Systeme integriert werden kann. Gleichzeitig bleibt die Weiterentwicklung offen für zukünftige Erweiterungen, etwa für differenziertere Aktivitätszustände oder Audit-Informationen.
Erweiterungen der Admin-REST-API
Das Aktiv/Inaktiv-Modell entfaltet seine volle Wirkung erst durch die Erweiterungen der administrativen REST-API. Diese bildet die Verbindung zwischen dem Datenmodell, der internen Geschäftslogik und den verschiedenen Clients, die Shortlinks erzeugen, bearbeiten oder automatisiert verwalten. Damit Benutzer den neuen Aktivitätsstatus effektiv nutzen können, mussten mehrere API-Endpunkte angepasst oder ergänzt werden.
Kapitel 4 beleuchtet diese Erweiterungen im Detail. Es zeigt, wie das neue Toggle-Endpoint den gezielten Wechsel des Aktivitätsstatus ermöglicht, wie inaktive Einträge über ein spezielles Listen-Endpoint abgefragt werden können und wie der ActiveState-Filter die bestehenden Abfrageoperationen erweitert. Ebenso wird erläutert, wie durch differenzierte HTTP-Statuscodes die semantische Bedeutung eines deaktivierten oder abgelaufenen Shortlinks klar kommuniziert wird.
Mit diesen API-Anpassungen entsteht eine kohärente, gut integrierte Schnittstelle, über die alle Aspekte des Aktiv/Inaktiv-Modells abbildbar sind – unabhängig davon, ob die Anfragen aus einer Benutzeroberfläche, einem Java-Client oder einem automatisierten System stammen.
Neues Toggle-Endpoint
Um das Aktiv/Inaktiv-Modell für Shortlinks in der gesamten Anwendung nutzbar zu machen, benötigt die Serverkomponente einen klar definierten Mechanismus, mit dem der Aktivitätsstatus eines bestehenden Shortlinks geändert werden kann. Genau hierfür wurde ein neues Toggle-Endpoint eingeführt, das serverseitig durch den ToggleActiveHandler umgesetzt wird. Es bildet die Grundlage dafür, dass Benutzer den Aktivitätszustand eines Shortlinks direkt über die REST-API anpassen können – sei es aus der Benutzeroberfläche, aus Automatisierungen oder aus externen Systemen.
Das neue Endpoint verfolgt einen klaren Zweck: Es ermöglicht das gezielte Umschalten des Aktivitätsstatus eines bestimmten Shortlinks. Der Benutzer kann über einen einfachen Request festlegen, ob ein Shortlink aktiviert oder deaktiviert werden soll, ohne andere Eigenschaften des Mappings verändern zu müssen. Dadurch wird die Bedienung nicht nur effizienter, sondern auch weniger fehleranfällig, da keine vollständigen Update-Payloads erforderlich sind.
Dieses Endpoint fügt sich nahtlos in die bestehende API-Struktur ein und nutzt dieselben transportierten Modelle und Validierungsmechanismen wie die anderen administrativen Operationen. Gleichzeitig bietet es eine klare Trennung zwischen der Änderung des Aktivitätsstatus und anderen Bearbeitungsvorgängen, was sowohl die Implementierung als auch die Nutzung vereinfacht.
Der zentrale Baustein für dieses Endpoint ist der ToggleActiveHandler. Er kapselt die HTTP-spezifische Verarbeitung und delegiert die eigentliche Statusänderung an den dahinterliegenden UrlMappingStore:
public class ToggleActiveHandler
implements HttpHandler, HasLogger {
private final UrlMappingStore store;
public ToggleActiveHandler(UrlMappingStore store) {
this.store = store;
}
@Override
public void handle(HttpExchange ex)
throws IOException {
logger().info("handle ... {} ", ex.getRequestMethod());
if (!RequestMethodUtils.requirePut(ex)) return;
try {
final String body = readBody(ex.getRequestBody());
ToggleActiveRequest req = fromJson(body, ToggleActiveRequest.class);
var shortCode = req.shortCode();
if (isNullOrBlank(shortCode)) {
writeJson(ex, BAD_REQUEST, "Missing 'shortCode'");
return;
}
boolean newActiveValue = req.active();
logger().info("Toggling active status for {} to {}", shortCode, newActiveValue);
Result<ToggleActiveResponse> mapping = store.toggleActive(shortCode, newActiveValue);
if (mapping.isPresent()) {
logger().info("Toggling active status successfully");
writeJson(ex, OK, toJson(mapping.get()));
} else {
mapping.
ifFailed(failed -> logger().info("Toggling active status failed: {}", failed));
writeJson(ex, BAD_REQUEST, "Toggling active status failed");
}
} catch (RuntimeException e) {
logger().warn("catch - {}", e.toString());
writeJson(ex, INTERNAL_SERVER_ERROR);
} finally {
logger().info("ToggleActiveHandler .. finally");
ex.close();
}
}
}
Der Handler implementiert das Interface HttpHandler und bindet zugleich das projektspezifische Logging über HasLogger ein. Im Konstruktor wird die Abhängigkeit zum UrlMappingStore injiziert, über den später die eigentliche Fachlogik zur Statusänderung ausgeführt wird.
Der Hauptteil der Logik befindet sich im try-Block. Zuerst wird der Request-Body über readBody(ex.getRequestBody()) vollständig eingelesen und mittels fromJson in eine Instanz von ToggleActiveRequest überführt. Dieses Request-Objekt enthält die beiden relevanten Informationen: den shortCode und den neuen active-Wert. Anschließend wird geprüft, ob der shortCode leer oder null ist. In diesem Fall antwortet der Handler sofort mit einem 400 Bad Request und einer klaren Fehlermeldung – so wird verhindert, dass die Store-Schicht mit ungültigen Daten konfrontiert wird.
Ist der ShortCode gültig, wird der gewünschte neue Aktivitätswert extrahiert und im Log festgehalten. Die eigentliche Statusänderung erfolgt dann über „store.toggleActive(shortCode, newActiveValue)“. Das Ergebnis wird in einem Result<ToggleActiveResponse> gekapselt, sodass sowohl Erfolgs- als auch Fehlerfälle einheitlich behandelt werden können.
Im Erfolgsfall (mapping.isPresent()) wird nochmals ein Erfolgseintrag ins Log geschrieben und die aktualisierte Darstellung des Shortlinks als JSON mit dem HTTP-Status 200 OK an den Benutzer zurückgegeben. Falls der Store kein Ergebnis liefert, wird über ifFailed die Fehlerursache geloggt und dem Benutzer eine Antwort mit dem Statuscode 400 Bad Request und einer generischen Fehlermeldung gesendet. Damit ist das Fehlerverhalten klar definiert, ohne dabei intern zu viele Details preiszugeben.
Treten unerwartete Laufzeitfehler im Handler selbst auf, fängt der Catch-Block die Ausnahme ab, schreibt eine Warnung ins Log und antwortet mit 500 Internal Server Error. Der finally-Block stellt sicher, dass die Verbindung über ex.close() in jedem Fall sauber geschlossen wird und ein entsprechender Log-Eintrag erstellt wird. Insgesamt entsteht so ein robustes, klar strukturiertes Endpoint, das den Aktivitätsstatus eines Shortlinks zuverlässig und nachvollziehbar umschaltet.
Abfrage inaktiver Links
Neben dem gezielten Umschalten des Aktivitätsstatus eines einzelnen Shortlinks benötigt der Benutzer auch die Möglichkeit, gesammelte inaktive Einträge einzusehen. Dies ermöglicht einen Überblick über Shortlinks, die bewusst oder automatisiert deaktiviert wurden und derzeit keine Weiterleitungen mehr durchführen.
Für diesen Zweck wurde ein spezielles API-Endpoint eingeführt, das ausschließlich inaktive Shortlinks zurückgibt. Dieses Endpoint dient als Ergänzung zum allgemeinen Listen-Endpoint und bietet dem Benutzer eine klare, gefilterte Sicht auf alle Shortlinks mit dem Status „inaktiv“.
Der Fokus dieses Endpoints liegt auf Transparenz und Kontrolle. Benutzer können schnell erkennen, welche Shortlinks derzeit deaktiviert sind, ohne eigene Filterlogiken implementieren oder die vollständige Liste aller Shortlinks auswerten zu müssen. Besonders in größeren Installationen oder automatisierten Workflows ist dies ein spürbarer Vorteil, da inaktive Einträge häufig eigene Verwaltungsprozesse erfordern.
Das Endpoint reiht sich nahtlos in die bestehende Struktur der administrativen API ein und nutzt dieselben Datenmodelle und Rückgabeformate wie die anderen Abfrageoperationen. Es stellt sicher, dass die Informationen zu inaktiven Shortlinks unabhängig von der UI oder dem Client konsistent abrufbar sind.
Die technische Umsetzung der Abfrage inaktiver Shortlinks ist in den bestehenden Listen-Handler integriert. Der folgende Ausschnitt zeigt den dafür verantwortlichen ListHandler, der unterschiedliche Listenvarianten, darunter auch die inaktiven Einträge, über ein gemeinsames Endpoint bereitstellt:
public class ListHandler
implements HttpHandler, HasLogger {
private final UrlMappingLookup store;
public ListHandler(UrlMappingLookup store) {
this.store = store;
}
// SNIPP
@Override
public void handle(HttpExchange ex)
throws IOException {
if (!RequestMethodUtils.requireGet(ex)) return;
final String path = ex.getRequestURI().getPath();
logger().info("List request: {}", path);
String responseJson;
if (path.endsWith(PATH_ADMIN_LIST_ALL)) {
responseJson = listAll();
} else if (path.endsWith(PATH_ADMIN_LIST_EXPIRED)) {
responseJson = listExpired();
} else if (path.endsWith(PATH_ADMIN_LIST_ACTIVE)) {
responseJson = listActive();
} else if (path.endsWith(PATH_ADMIN_LIST_INACTIVE)) {
responseJson = listInActive();
} else if (path.endsWith(PATH_ADMIN_LIST)) {
responseJson = listFiltered(ex);
} else {
logger().info("undefined path {}", path);
ex.sendResponseHeaders(404, -1);
return;
}
writeJson(ex, OK, responseJson);
}
private String listInActive() {
final Instant now = Instant.now();
return filterAndBuild("inactive",
m -> {
if (!m.active()) return true;
return m.expiresAt()
.map(exp -> exp.isBefore(now))
.orElse(false);
});
}
private String filterAndBuild(String mode, Predicate<ShortUrlMapping> predicate) {
final Instant now = Instant.now();
final var data = store
.findAll()
.stream()
.filter(predicate)
.map(m -> toDto(m, now))
.toList();
return JsonUtils.toJsonListing(mode, data.size(), data);
}
private Map<String, String> toDto(ShortUrlMapping m, Instant now) {
boolean expired = m.
expiresAt()
.map(t -> t.isBefore(now))
.orElse(false);
return Map.of(
"shortCode", m.shortCode(),
"originalUrl", m.originalUrl(),
"createdAt", m.createdAt().toString(),
"expiresAt", m.expiresAt().map(Instant::toString).orElse(""),
"active", m.active() + "",
"status", expired ? "expired" : "active"
);
}
}
Der ListHandler implementiert, wie auch der Toggle-Handler, das HttpHandler-Interface und nutzt HasLogger für konsistentes Logging. Im Konstruktor wird ein UrlMappingLookup injiziert, über den die eigentlichen Datenabfragen erfolgen. Die handle-Methode übernimmt das Routing auf Basis des Request-Pfads: Je nachdem, ob die Anfrage etwa auf PATH_ADMIN_LIST_ALL, PATH_ADMIN_LIST_ACTIVE oder PATH_ADMIN_LIST_INACTIVE endet, wird eine spezialisierte Listenmethode aufgerufen.
Für die Abfrage inaktiver Shortlinks ist die Methode listInActive() zuständig. Sie ermittelt den aktuellen Zeitpunkt und ruft filterAndBuild("inactive", ...) mit einem Prädikat auf, das die gewünschten Einträge auswählt. Ein Shortlink gilt als inaktiv, wenn er entweder explizit als nicht aktiv markiert ist (!m.active()) oder zwar aktiv ist, aber bereits abgelaufen ist. Die Kombination aus Aktiv-Flag und Ablaufdatum ermöglicht eine saubere Trennung zwischen aktuell nutzbaren und nicht mehr verwendbaren Einträgen.
Die Methode filterAndBuild umfasst das Muster, alle Einträge aus dem UrlMappingLookup zu lesen, das übergebene Prädikat anzuwenden und die verbleibenden Mappings in eine DTO-ähnliche Struktur zu überführen. Dafür wird toDto verwendet, das aus jedem ShortUrlMapping eine Map mit den wichtigsten Attributen erstellt. Neben shortCode, originalUrl, createdAt und expiresAt werden hier auch der active-Status sowie ein abgeleitetes Feld status gesetzt, das zwischen expired und active unterscheidet. Diese zusätzliche Information erleichtert dem Benutzer, den Zustand eines Eintrags direkt aus der Antwort abzulesen.
Durch diese Struktur fügt sich die Abfrage inaktiver Links nahtlos in das bestehende Listenkonzept ein. Statt ein völlig eigenständiges Endpoint einzuführen, wird die bereits vorhandene Infrastruktur genutzt und um eine gezielte Sicht auf inaktive Einträge erweitert. Das Ergebnis ist ein klarer, wiederverwendbarer Mechanismus, mit dem Benutzer jederzeit einen vollständigen Überblick über alle aktuell inaktiven Shortlinks erhalten.
Anpassungen im List-Request (ActiveState-Filter)
Damit Benutzer gezielt nach aktiven, inaktiven oder allen Shortlinks suchen können, wurde der bestehende Listenmechanismus um einen dedizierten Filter für den Aktivitätsstatus erweitert. Dieser sogenannte ActiveState-Filter ergänzt die bisherigen Filterkriterien wie Textsuche, Sortierung, Zeiträume oder Paginierung und ermöglicht eine präzise Steuerung des Suchergebnisses über die API.
Ziel dieser Erweiterung ist es, dem Benutzer eine flexible und dennoch klar definierte Möglichkeit zu geben, den gewünschten Status direkt als Query-Parameter festzulegen. Statt die Gesamtliste aller Shortlinks abzurufen und anschließend clientseitig zu filtern, kann die Anfrage jetzt direkt serverseitig eingeschränkt werden. Dies spart Ressourcen, reduziert unnötige Datenübertragungen und sorgt für ein konsistentes Suchergebnis.
Der ActiveState-Filter berücksichtigt dabei drei mögliche Zustände:
- Aktiv – Der Shortlink ist aktiv und, sofern ein Ablaufdatum vorhanden ist, noch nicht abgelaufen.
- Inaktiv – Der Shortlink wurde vom Benutzer deaktiviert oder ist abgelaufen.
- Nicht gesetzt – Der Benutzer macht keine Statusangabe, sodass der Filter nicht angewendet wird und alle relevanten Einträge berücksichtigt werden.
Diese Unterscheidung wird im Query-Parameter active übermittelt, das als Teil einer GET-Anfrage an das allgemeine Listen-Endpoint übergeben wird. Der Server wertet diesen Parameter aus und baut daraus ein entsprechendes UrlMappingFilter-Objekt, das die Auswahl der Einträge steuert.
Die technische Umsetzung dieser Filterlogik erfolgt im bestehenden ListHandler, der bereits für das Filtern und Sortieren von Shortlinks verantwortlich ist. Der folgende Auszug zeigt, wie der active-Parameter verarbeitet und in den UrlMappingFilter eingebettet wird:
var filter = UrlMappingFilter.builder()
.codePart(first(query, "code"))
.urlPart(first(query, "url"))
.createdFrom(parseInstant(first(query, "from"), true).orElse(null))
.createdTo(parseInstant(first(query, "to"), false).orElse(null))
.active(parseBoolean(first(query, "active")).orElse(null))
.offset(offset)
.limit(size)
.sortBy(sortBy.orElse(null))
.direction(dir.orElse(null))
.build();
Die zentrale Stelle ist die Zeile:
.active(parseBoolean(first(query, "active")).orElse(null))
Hier wird der Query-Parameter active ausgelesen, der als optionaler Boolean interpretiert wird. Der Ablauf im Detail:
first(query, "active")liest den ersten Wert des Query-Parametersactiveaus – etwa:active=true
active=false
- oder der Parameter fehlt vollständig.
parseBoolean(...)wandelt diesen Wert in einOptional<Boolean>um. Dadurch können auch ungültige oder fehlende Werte sauber abgefangen werden..orElse(null)sorgt dafür, dass bei fehlendem oder nicht interpretierbarem Input der Wertnullin den Filter übertragen wird.
Damit ergibt sich folgende Bedeutung:
active=true→ nur aktive Shortlinksactive=false→ nur inaktive Shortlinks- kein Parameter → keine Einschränkung
Der resultierende UrlMappingFilter wird anschließend vom UrlMappingLookup verarbeitet:
int total = store.count(filter);
var results = store.find(filter);
Diese Methoden übernehmen die eigentliche Anwendung des Filters auf die gespeicherten Shortlinks. Durch die Übergabe des aktiven Zustands kann die Lookup-Schicht präzise die Einträge auswählen, die dem gewünschten Aktivitätsstatus entsprechen.
Abschließend werden die Ergebnisse in die DTO-Struktur überführt und als paginierte JSON-Antwort zurückgegeben:
var items = results.stream().map(m -> toDto(m, now)).toList();
return toJsonListingPaged("filtered", items.size(), items, page, size, total, sortBy.orElse(null), dir.orElse(null));
Durch diese Erweiterung fügt sich der ActiveState-Filter nahtlos in das bestehende Filtersystem ein. Benutzer können jetzt gezielt nach aktiven oder inaktiven Shortlinks suchen, ohne zusätzliche Endpunkte oder separate Aufrufe verwenden zu müssen. Dabei wird sichtbar, wie ActiveState verarbeitet wird und wie er die Zusammenstellung des finalen JSON-Ergebnisses beeinflusst.
HTTP-Statuscode-Semantik: 410 vs. 404
Mit der Einführung des Aktiv/Inaktiv-Modells verändert sich auch das Verhalten der Weiterleitungslogik. Für den Benutzer ist es wichtig, dass der Zustand eines Shortlinks klar und eindeutig nach außen kommuniziert wird – insbesondere dann, wenn eine Weiterleitung nicht wie erwartet erfolgen kann. Aus diesem Grund nutzt das System zwei unterschiedliche HTTP-Statuscodes, um abgelaufene und deaktivierte Shortlinks voneinander zu unterscheiden.
Ein Shortlink kann aus zwei Gründen nicht mehr erreichbar sein:
- Er ist abgelaufen. Das Ablaufdatum liegt in der Vergangenheit, und der Shortlink ist damit planmäßig nicht mehr gültig.
- Er wurde deaktiviert. Der Benutzer hat den Shortlink manuell deaktiviert, ohne dass das Ablaufdatum eine Rolle spielt.
Obwohl beide dazu führen, dass keine Weiterleitung erfolgt, unterscheiden sie sich in ihrer Bedeutung. Abgelaufene Shortlinks sollen klar signalisieren, dass ihre Lebensdauer regulär endet. Deaktivierte Shortlinks wurden hingegen bewusst außer Betrieb genommen und befinden sich möglicherweise in einem temporären Zustand.
Diese semantische Trennung wird durch zwei HTTP-Statuscodes abgebildet:
- 410 Gone – für abgelaufene Shortlinks. Dieser Status zeigt an, dass die Ressource dauerhaft nicht mehr verfügbar ist und nicht wieder verfügbar wird.
- 404 Not Found – für deaktivierte Shortlinks. Obwohl der Shortlink existiert, wird seine Weiterleitung absichtlich nicht durchgeführt. Der 404-Status signalisiert einen temporären oder dauerhaften Zustand.
Durch diese Unterscheidung erhalten Benutzer, API-Clients und Monitoring-Systeme eine präzisere Rückmeldung zum Zustand des Shortlinks. Es ist nachvollziehbar, dass ein Shortlink aufgrund eines natürlichen Ablaufs oder einer manuellen Entscheidung nicht erreichbar ist.
Cheers Sven