~/home of geeks

Last ID Standing

· 380 Wörter · 2 Minute(n) Lesedauer

Bei Datenmigrationen, die über einen längeren Zeitraum laufen, ist es sinnvoll, den Migrator so zu gestalten, dass er zwischendurch beendet und neu gestartet werden kann. Ein Hilfsmittel hierzu ist eine Klasse, welche sich die ID der zuletzt migrierten Datensatzes persistent merkt.

Hinweis: Daten müssen wohl sortiert migriert werden.

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import javax.annotation.PostConstruct;

import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

@Log4j2
@Component
public class LastSuccessfulId {

    public static final File DEFAULT_STORE_FILE = new File("./lastSuccessfulId.txt");

    @SuppressWarnings("squid:S3749") // Muss nicht autowired werden
    private String id;

    @SuppressWarnings("squid:S3749") // Muss nicht autowired werden
    @Getter
    private final File file;

    public LastSuccessfulId() {
        file = DEFAULT_STORE_FILE;
    }

    /**
     * Erzeugt eine Instanz mit der angegebenen Datei als Store.
     *
     * @param storeFile Datei zum Laden / Speichern der letzten verarbeiteten ID.
     */
    public LastSuccessfulId(File storeFile) {
        file = storeFile;
    }

    /**
     * Liest die als letztes verarbeitete Id aus Hilfsdatei ein.<br>
     * Existiert die Datei nicht, oder ist sie leer, wird {@code null zurück geliefert}.
     */
    @PostConstruct
    public synchronized void initialize() {
        id = readFromFile();
    }

    /**
     * Liefert die ID des letzten verarbeiteten Datensatzes.
     *
     * @return ID des letzten verarbeiteten Datensatzes.
     */
    public synchronized String getId() {
        return id;
    }

    /**
     * Setzt die ID des letzten verarbeiteten Datensatzes.<br>
     * Die ID wird nur übernommen, wenn sue größer las die letzte gespeicherte ID ist.<br>
     * Die ID wird direkt in die Datei geschrieben.
     *
     * @param newId Neue, zu verwendende ID.
     * @return Diese Instanz des Helfers.
     */
    public synchronized LastSuccessfulId setId(final String newId) throws IOException {
        if (newId != null) {
            log.debug("writeLastSuccesfulId: {}", newId);
            // Funktioniert nur, weil die UUIDs sortiert (als String) aus der Datenquelle gelesen werden.
            if (id == null || newId.compareTo(id) > 0) {
                FileUtils.writeStringToFile(file, newId, StandardCharsets.UTF_8.name());
                id = newId;
            }
        }
        return this;
    }

    /**
     * Liest die als letztes verarbeitete Id aus Hilfsdatei ein.<br>
     * Existiert die Datei nicht, oder ist sie leer, wird {@code null zurück geliefert}.
     *
     * @return Ermittelt die zuletzt erfolgreich verarbeitete UUID.
     */
    private synchronized String readFromFile() {
        if (!file.exists() || !file.canRead()) {
            return null;
        }

        try {
            final String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8.name());
            if (!StringUtils.isEmpty(content)) {
                return content.trim();
            }
        } catch (final IOException ioe) {
            log.error(ioe.getMessage(), ioe);
        }
        return null;
    }

}