Files
agiledancers/agiledancers-be/src/main/java/nl/agilecc/agiledancers/service/SchoolTransactions.java
Wanja Krah 694bf3fa86
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
S16 postgresql opslag added
2026-11-04 17:10:25 +01:00

119 lines
4.9 KiB
Java

package nl.agilecc.agiledancers.service;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.List;
import java.util.concurrent.Callable;
import nl.agilecc.agiledancers.model.DanceSchool;
import nl.agilecc.agiledancers.repository.Database;
import nl.agilecc.agiledancers.repository.RevisionConflictException;
import nl.agilecc.agiledancers.repository.SchoolReader;
import nl.agilecc.agiledancers.repository.SchoolWriter;
import nl.agilecc.agiledancers.repository.StorageException;
import nl.agilecc.agiledancers.service.model.SchoolChange;
/** Serializes short domain units of work; solver computation never holds this lock or a database connection. */
@Singleton
public class SchoolTransactions {
private final SchoolManagementService schools;
private final Database database;
private final SchoolReader reader;
private final SchoolWriter writer;
@Inject
public SchoolTransactions(
final SchoolManagementService schools,
final Database database,
final SchoolReader reader,
final SchoolWriter writer) {
this.schools = schools;
this.database = database;
this.reader = reader;
this.writer = writer;
}
/** An expected revision is mandatory for existing-school writes; null school identifies a new-school operation. */
public Object request(final Long schoolId, final Long revision, final boolean mutation, final Callable<Object> work)
throws Exception {
return request(new SchoolChange(schoolId, revision, mutation, mutation), work);
}
public Object request(final SchoolChange change, final Callable<Object> work) throws Exception {
synchronized (schools) {
try {
return database.transaction(() -> {
final List<DanceSchool> before = reader.load();
schools.load(before, database::nextId);
if (change.mutation() && change.schoolId() != null) {
check(change);
schools.school(change.schoolId())
.setRevision(change.revision() + (change.changesInputs() ? 1 : 0));
}
final Object result = work.call();
if (change.changesInputs()) {
store(before, change.schoolId());
}
return result;
});
} catch (final RevisionConflictException failure) {
throw new ManagementException(ManagementExceptionKind.CONFLICT, failure.getMessage());
} catch (final StorageException failure) {
throw new ManagementException(ManagementExceptionKind.STORAGE_UNAVAILABLE, failure.getMessage());
}
}
}
private void check(final SchoolChange change) {
final long schoolId = change.schoolId();
final Long expected = change.revision();
final long actual = schools.school(schoolId).getRevision();
if (expected == null || actual != expected) {
throw new ManagementException(
ManagementExceptionKind.CONFLICT,
"Deze school is gewijzigd of de schoolversie ontbreekt. Herlaad de gegevens en probeer opnieuw.");
}
writer.checkRevision(schoolId, expected);
}
private void store(final List<DanceSchool> before, final Long schoolId) {
final List<DanceSchool> after = schools.schools();
if (schoolId != null) {
final DanceSchool retained = after.stream()
.filter((final DanceSchool school) -> school.getId().equals(schoolId))
.findFirst()
.orElse(null);
if (retained == null) {
writer.delete(schoolId);
} else {
writer.save(retained);
}
}
final List<Long> known = before.stream().map(DanceSchool::getId).toList();
after.stream()
.filter((final DanceSchool school) -> !known.contains(school.getId()))
.forEach(writer::save);
}
/** Refreshes current domain state and commits a worker's atomic result/status operation. */
public <T> T background(final Callable<T> work) {
synchronized (schools) {
try {
return database.transaction(() -> {
schools.load(reader.load(), database::nextId);
return work.call();
});
} catch (final RuntimeException failure) {
throw failure;
} catch (final Exception failure) {
throw new IllegalStateException(failure);
}
}
}
public void saveResult(final long schoolId) {
final DanceSchool school = schools.school(schoolId);
school.setRevision(school.getRevision() + 1);
writer.save(school);
}
}