RepositoryImpl.java
package io.github.jonloucks.contracts.impl;
import io.github.jonloucks.contracts.api.*;
import java.util.*;
import static io.github.jonloucks.contracts.api.Checks.*;
import static java.util.Optional.ofNullable;
/**
* Implementation for {@link io.github.jonloucks.contracts.api.Repository}
* @see io.github.jonloucks.contracts.api.Repository
*/
final class RepositoryImpl implements Repository {
@Override
public AutoClose open() {
if (openState.transitionToOpen()) {
storedContracts.values().forEach(StorageImpl::bind);
check();
return this::close;
}
return ()->{};
}
@Override
public <T> AutoClose store(Contract<T> contract, Promisor<T> promisor, BindStrategy bindStrategy) {
final Contract<T> validContract = contractCheck(contract);
final Promisor<T> validPromisor = promisorCheck(promisor);
final BindStrategy validBindStrategy = nullCheck(bindStrategy, "bindStrategy");
if (storedContracts.containsKey(validContract) && openState.isOpen()) {
throw new ContractException( "The contract " + validContract + " is already stored.");
}
final StorageImpl<T> storage = new StorageImpl<>(contracts, validContract, validPromisor, validBindStrategy);
if (openState.isOpen()) {
storage.bind();
}
storedContracts.put(validContract, storage);
return () -> {
if (storedContracts.remove(validContract, storage)) {
storage.close();
}
};
}
@Override
public void check() {
requiredContracts.forEach(contract -> {
if (!contracts.isBound(contract)) {
throw new ContractException( "The contract " + contract + " is required.");
}
});
}
@Override
public <T> void require(Contract<T> contract) {
final Contract<T> validContract = contractCheck(contract);
requiredContracts.add(validContract);
}
RepositoryImpl(Contracts contracts) {
this.contracts = contracts;
}
private void close() {
if (openState.transitionToClosed()) {
reverseCloseStorage();
}
}
private void reverseCloseStorage() {
final Stack<StorageImpl<?>> storageStack = new Stack<>();
storedContracts.values().forEach(storageStack::push);
try {
while (!storageStack.isEmpty()) {
storageStack.pop().close();
}
} finally {
storedContracts.clear();
}
}
/**
* Using LinkedHashMap to retain insertion order
*/
private final Map<Contract<?>, StorageImpl<?>> storedContracts = new LinkedHashMap<>();
private static final class StorageImpl<T> implements AutoClose {
StorageImpl(Contracts contracts, Contract<T> contract, Promisor<T> promisor, BindStrategy bindStrategy) {
this.contracts = contracts;
this.contract = contract;
this.promisor = promisor;
this.bindStrategy = bindStrategy;
}
private void bind() {
close();
closeBinding = contracts.bind(contract, promisor, bindStrategy);
}
@Override
public void close() {
ofNullable(closeBinding).ifPresent(close -> {
this.closeBinding = null;
close.close();
});
}
private final Contract<T> contract;
private final Promisor<T> promisor;
private final BindStrategy bindStrategy;
private final Contracts contracts;
private AutoClose closeBinding;
}
private final Contracts contracts;
private final IdempotentImpl openState = new IdempotentImpl();
private final Set<Contract<?>> requiredContracts = new HashSet<>();
}