All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
119 lines
4.9 KiB
Java
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);
|
|
}
|
|
}
|