Commit 0cb2ed29 authored by Luna Riegel's avatar Luna Riegel
Browse files

Refactor: Add state management to CityObjectCaches

parent fd7858f8
package de.hft.stuttgart.citydoctor2.database; package de.hft.stuttgart.citydoctor2.database;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import java.util.List;
/** /**
* Utility class for accessing the FeatureCache instance. Enables access to the cache in contexts without reference or * Utility class for accessing and managing the FeatureCache instance. Enables access to the cache in contexts without
* pass through of the {@link de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel CityDoctorModel}. * reference or pass through of the {@link de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel CityDoctorModel}.
*/ */
public class CacheManager { public class CacheManager {
private static FeatureCache cache = null;
private static final FeatureCache cache = new FeatureCache();
private CacheManager() {} private CacheManager() {}
...@@ -19,31 +16,9 @@ public class CacheManager { ...@@ -19,31 +16,9 @@ public class CacheManager {
* Returns the FeatureCache instance * Returns the FeatureCache instance
*/ */
public static FeatureCache getFeatureCache() { public static FeatureCache getFeatureCache() {
if (cache == null){
cache = new FeatureCache();
}
return cache; return cache;
} }
/**
* Signals the cache that parsing has finished
*/
public static void finishParsing(){
if (cache != null){
cache.finishParsing();
}
}
/**
* Signals the cache to synchronize its content with the database.
*/
public static void synchronizeCache(){
if (cache != null){
cache.synchronize();
}
}
/** /**
* Returns a new UnconnectedCache Object. * Returns a new UnconnectedCache Object.
* @return * @return
......
...@@ -9,7 +9,7 @@ import java.util.stream.Stream; ...@@ -9,7 +9,7 @@ import java.util.stream.Stream;
public interface CityObjectCache { public interface CityObjectCache {
/** /**
* Adds a CityObject to this cache, overwriting existing CityObjects with the same GmlID * Inserts a CityObject into this cache. Overwrites any existing CityObject with the same GmlID.
* @param cityObject * @param cityObject
*/ */
void put(CityObject cityObject); void put(CityObject cityObject);
...@@ -54,4 +54,16 @@ public interface CityObjectCache { ...@@ -54,4 +54,16 @@ public interface CityObjectCache {
*/ */
List<GmlId> getErrorFeatureIds(); List<GmlId> getErrorFeatureIds();
/**
* Sets this cache into parsing mode, returning an AutoClosable representing the scoped-state of parsing operation.
* @return an AutoClosable representing the scoped-state for parsing
*/
AutoCloseable startParsingMode();
/**
* Sets this cache into checking mode, returning an AutoClosable representing the scoped-state of checking operation.
* @return an AutoClosable representing the scoped-state for checking
*/
AutoCloseable startCheckingMode();
} }
...@@ -11,20 +11,21 @@ import org.apache.logging.log4j.Logger; ...@@ -11,20 +11,21 @@ import org.apache.logging.log4j.Logger;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* A managed cache for CityObjects. This cache is connected to the embedded database and will automatically load missing * A managed cache for top-level CityObjects (Features). This cache is connected to the embedded database and will
* CityObjects from it. Has a hard limit on concurrently held CityObjects and will write them back to the database upon * automatically load missing CityObjects from it. This cache is size limited and will evict entries using the
* eviction. * Window Tiny LFU policy.
* <p> * <p>
* This cache may only have a single instance, use {@link CacheManager} for instantiation. * This cache may only have a single instance, use {@link CacheManager} for instantiation and access.
* <p/> * <p/>
* <p> * <p>
* Avoid holding long-time strong references to CityObjects returned by the cache, as eviction will sever the * Avoid holding long-time strong references to CityObjects returned by the cache, as eviction will sever the
* connection between reference and cache, which can lead to conflicting Object states. * connection between reference and cache, which will lead to conflicting Object states.
* <p/> * <p/>
*/ */
public class FeatureCache implements CityObjectCache{ public class FeatureCache implements CityObjectCache{
...@@ -35,32 +36,50 @@ public class FeatureCache implements CityObjectCache{ ...@@ -35,32 +36,50 @@ public class FeatureCache implements CityObjectCache{
private final LoadingCache<GmlId, CityObject> cache; private final LoadingCache<GmlId, CityObject> cache;
private final EmbeddedDatabaseHandler handler; private final EmbeddedDatabaseHandler handler;
private final AtomicInteger putsCounter = new AtomicInteger(0); private final AtomicInteger putsCounter = new AtomicInteger(0);
private boolean parsingFinished = false; private CacheState state = CacheState.IDLE;
private final ConcurrentHashMap<Thread, GmlId> evictionLocks = new ConcurrentHashMap<>();
FeatureCache() { FeatureCache() {
handler = new EmbeddedDatabaseHandler(); handler = new EmbeddedDatabaseHandler();
cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> { cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> {
if (value != null && cause.wasEvicted()) { if (value != null && cause.wasEvicted() && isFeatureMarshallable(value)){
handler.marshallCityObject(value); handler.marshallCityObject(value);
} }
}).build(handler::unmarshallCityObject); }).build(handler::unmarshallCityObject);
} }
/**
* Checks if the CityObject is currently being checked by a Checker-thread to prevent concurrent modifications by the
* Marshaller on eviction.
* @param co the CityObject
* @return true if the CityObject can be marshalled, false otherwise
*/
private boolean isFeatureMarshallable(CityObject co) {
return switch (state){
case IDLE, PARSING -> true;
case CHECKING -> ! (evictionLocks.containsValue(co.getGmlId()));
};
}
@Override @Override
public void put(CityObject cityObject) { public void put(CityObject cityObject) {
cache.put(cityObject.getGmlId(), cityObject); cache.put(cityObject.getGmlId(), cityObject);
/* Batch marshall during parsing to improve performance switch (state){
*/ case IDLE:
if (!parsingFinished){ break;
case PARSING:
int count = putsCounter.incrementAndGet(); int count = putsCounter.incrementAndGet();
if (count >= 2500) { if (count >= 2500) {
batchMarshall(); batchMarshall();
} }
break;
case CHECKING:
evictionLocks.remove(Thread.currentThread());
break;
} }
} }
private void batchMarshall(){ private void batchMarshall(){
logger.trace("Setting up batch Marshaller"); logger.trace("Setting up batch Marshaller");
Map<GmlId, CityObject> cacheContent= new HashMap<>(cache.asMap()); Map<GmlId, CityObject> cacheContent= new HashMap<>(cache.asMap());
...@@ -84,6 +103,9 @@ public class FeatureCache implements CityObjectCache{ ...@@ -84,6 +103,9 @@ public class FeatureCache implements CityObjectCache{
@Override @Override
public CityObject get(GmlId id) { public CityObject get(GmlId id) {
if (state == CacheState.CHECKING){
evictionLocks.put(Thread.currentThread(), id);
}
return cache.get(id); return cache.get(id);
} }
...@@ -92,7 +114,7 @@ public class FeatureCache implements CityObjectCache{ ...@@ -92,7 +114,7 @@ public class FeatureCache implements CityObjectCache{
@Override @Override
public void replace(GmlId id, CityObject cityObject) { public void replace(GmlId id, CityObject cityObject) {
cache.invalidate(id); cache.invalidate(id);
put(cityObject); cache.put(cityObject.getGmlId(), cityObject);
} }
@Override @Override
...@@ -110,12 +132,12 @@ public class FeatureCache implements CityObjectCache{ ...@@ -110,12 +132,12 @@ public class FeatureCache implements CityObjectCache{
logger.trace("Resetting cache"); logger.trace("Resetting cache");
cache.invalidateAll(); cache.invalidateAll();
putsCounter.set(0); putsCounter.set(0);
parsingFinished = false;
} }
@Override @Override
public List<GmlId> getErrorFeatureIds() { public List<GmlId> getErrorFeatureIds() {
return handler.getIdsOfAllCityObjectsWithErrors(); return handler.getIdsOfAllCityObjectsWithErrors();
} }
/** /**
...@@ -132,15 +154,173 @@ public class FeatureCache implements CityObjectCache{ ...@@ -132,15 +154,173 @@ public class FeatureCache implements CityObjectCache{
} }
} }
/**
* Signals the cache that parsing is beginning.
*/
private boolean beginParsing(){
return state.changeState(this, CacheState.PARSING);
}
/** /**
* Signals the cache that parsing has finished. * Signals the cache that parsing has finished.
*/ */
public void finishParsing() { private void finishParsing() {
if (!parsingFinished) { if (state == CacheState.PARSING && state.changeState(this, CacheState.IDLE)){
synchronize(); return;
parsingFinished = true; }
throw new IllegalStateException("Cache failed to exit parsing mode");
}
/**
* Signals the cache that checking is beginning.
*/
private boolean beginChecking(){
return state.changeState(this, CacheState.CHECKING);
}
/**
* Signals the cache that checking has finished.
*/
private void finishChecking() {
if (state == CacheState.CHECKING && state.changeState(this, CacheState.IDLE)){
return;
}
throw new IllegalStateException("Cache failed to exit checking mode");
}
@Override
public AutoCloseable startParsingMode(){
if (this.beginParsing()){
return this::finishParsing;
} else throw new IllegalStateException("Cache is not in IDLE state, cannot start parsing mode");
}
@Override
public AutoCloseable startCheckingMode(){
if (this.beginChecking()){
return this::finishChecking;
} else throw new IllegalStateException("Cache is not in IDLE state, cannot start checking mode");
}
protected void setState(CacheState state) {
this.state = state;
}
public CacheState getState() {
return state;
}
public enum CacheState {
IDLE {
@Override
protected boolean changeState(FeatureCache cache, CacheState nextState) {
if (cache.getState() != this){
logger.debug("FeatureCache invoked changeState() of IDLE state while cache state is {}", cache.getState());
return false;
}
this.onExit(cache);
cache.setState(nextState);
nextState.onEntry(cache);
return true;
}
@Override
public void onEntry(FeatureCache cache) {
logger.trace("FeatureCache: Entering IDLE state");
// Idle state does nothing on entry
}
@Override
protected void onExit(FeatureCache cache) {
logger.trace("FeatureCache: Leaving IDLE state");
// Idle state does nothing on exit
}
}, PARSING {
@Override
protected boolean changeState(FeatureCache cache, CacheState nextState) {
if (cache.getState() != this){
logger.debug("FeatureCache invoked changeState() of PARSING state while cache state is {}", cache.getState());
return false;
}
boolean success = false;
switch (nextState){
case IDLE:
this.onExit(cache);
cache.setState(CacheState.IDLE);
success = true;
break;
case PARSING:
logger.debug("Tried to set cache to PARSING state while already parsing");
break;
case CHECKING:
logger.debug("Tried to set cache to CHECKING state while parsing");
break;
} }
return success;
}
@Override
public void onEntry(FeatureCache cache) {
logger.trace("FeatureCache: Entering PARSING state");
cache.resetCache();
}
@Override
protected void onExit(FeatureCache cache) {
logger.trace("FeatureCache: Leaving PARSING state");
cache.synchronize();
} }
}, CHECKING {
@Override
protected boolean changeState(FeatureCache cache, CacheState nextState) {
if (cache.getState() != this){
logger.debug("FeatureCache invoked changeState() of CHECKING state while cache state is {}", cache.getState());
return false;
}
boolean success = false;
switch (nextState){
case IDLE:
this.onExit(cache);
cache.setState(CacheState.IDLE);
success = true;
break;
case PARSING:
logger.debug("Tried to set cache to PARSING state while checking");
break;
case CHECKING:
logger.debug("Tried to set cache to CHECKING state while already checking");
break;
}
return success;
}
@Override
public void onEntry(FeatureCache cache) {
logger.trace("FeatureCache: Entering CHECKING state");
}
@Override
protected void onExit(FeatureCache cache) {
logger.trace("FeatureCache: Leaving CHECKING state");
cache.synchronize();
}
};
/**
* Signals the statemachine to change the state of the FeatureCache. Returns true if the state successfully
* changed, false otherwise with the FeatureCache remaining in its current state.
* @param cache the FeatureCache instance
* @param nextState the requested next state
* @return true if the cache successfully changed state, false otherwise
*/
protected abstract boolean changeState(FeatureCache cache, CacheState nextState);
protected abstract void onEntry(FeatureCache cache);
protected abstract void onExit(FeatureCache cache);
}
} }
package de.hft.stuttgart.citydoctor2.database; package de.hft.stuttgart.citydoctor2.database;
import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
...@@ -10,13 +11,19 @@ import java.util.Map; ...@@ -10,13 +11,19 @@ import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* A lightweight, unmanaged CityObjectCache that is not connected with the embedded Database. * An adapter implementation for CityObjectCache, wrapping the Interface around a simple HashMap.
* <p>
* This class is intended to allow parsing or checking {@link CityDoctorModel CityDoctorModels} in specific cases where the model
* must not use the {@link FeatureCache}, like resolving of {@link de.hft.stuttgart.citydoctor2.datastructure.LibraryObject LibraryObject}
* files or mocking of the Interface in unit tests.
*
*/ */
public class UnconnectedCache implements CityObjectCache{ public class UnconnectedCache implements CityObjectCache{
private final Map<GmlId, CityObject> cache; private final Map<GmlId, CityObject> cache;
private boolean idle = true;
UnconnectedCache() { public UnconnectedCache() {
cache = new HashMap<>(); cache = new HashMap<>();
} }
...@@ -32,7 +39,7 @@ public class UnconnectedCache implements CityObjectCache{ ...@@ -32,7 +39,7 @@ public class UnconnectedCache implements CityObjectCache{
@Override @Override
public void replace(GmlId id, CityObject cityObject) { public void replace(GmlId id, CityObject cityObject) {
// Not needed cache.put(id, cityObject);
} }
@Override @Override
...@@ -46,7 +53,7 @@ public class UnconnectedCache implements CityObjectCache{ ...@@ -46,7 +53,7 @@ public class UnconnectedCache implements CityObjectCache{
} }
public void resetCache(){ public void resetCache(){
// cache.clear();
} }
@Override @Override
...@@ -59,4 +66,20 @@ public class UnconnectedCache implements CityObjectCache{ ...@@ -59,4 +66,20 @@ public class UnconnectedCache implements CityObjectCache{
}); });
return errorFeatureIds; return errorFeatureIds;
} }
@Override
public AutoCloseable startParsingMode() {
if (idle){
idle = false;
return ()-> idle=true;
} else throw new IllegalStateException("Cache is not idle, could not start parsing mode");
}
@Override
public AutoCloseable startCheckingMode() {
if (idle){
idle = false;
return ()-> idle=true;
} else throw new IllegalStateException("Cache is not idle, could not start checking mode");
}
} }
...@@ -92,6 +92,7 @@ public class CityDoctorModel { ...@@ -92,6 +92,7 @@ public class CityDoctorModel {
genericObjects = new ArrayList<>(); genericObjects = new ArrayList<>();
globalErrors = new ArrayList<>(); globalErrors = new ArrayList<>();
this.cache = cache; this.cache = cache;
cache.resetCache();
} }
public boolean isValidated() { public boolean isValidated() {
......
...@@ -25,6 +25,7 @@ import java.util.List; ...@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import de.hft.stuttgart.citydoctor2.database.CacheManager; import de.hft.stuttgart.citydoctor2.database.CacheManager;
import de.hft.stuttgart.citydoctor2.database.UnconnectedCache;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.citygml4j.core.model.CityGMLVersion; import org.citygml4j.core.model.CityGMLVersion;
...@@ -172,11 +173,12 @@ public class Citygml3FeatureMapper extends ObjectWalker { ...@@ -172,11 +173,12 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private List<ResolvableReference> references = new ArrayList<>(); private List<ResolvableReference> references = new ArrayList<>();
private Map<Vertex, Vertex> vertexMap = new HashMap<>(); private Map<Vertex, Vertex> vertexMap = new HashMap<>();
public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean nested) { public Citygml3FeatureMapper(ParserConfiguration config, Path path, boolean unconnected) {
this.config = config; this.config = config;
this.directory = path.getParent(); this.directory = path.getParent();
if (nested){
model = new CityDoctorModel(config, path.toFile(), CacheManager.getUnconnectedCache()); if (unconnected){
model = new CityDoctorModel(config, path.toFile(), new UnconnectedCache());
} else { } else {
model = new CityDoctorModel(config, path.toFile()); model = new CityDoctorModel(config, path.toFile());
} }
......
...@@ -44,7 +44,6 @@ import javax.xml.validation.Schema; ...@@ -44,7 +44,6 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; import javax.xml.validation.Validator;
import de.hft.stuttgart.citydoctor2.database.CacheManager;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId; import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder; import de.hft.stuttgart.citydoctor2.datastructure.ImplicitGeometryHolder;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
...@@ -254,13 +253,14 @@ public class CityGmlParser { ...@@ -254,13 +253,14 @@ public class CityGmlParser {
* @param l Listener hook for tracking of parsing progress * @param l Listener hook for tracking of parsing progress
* @param handler Validation handler for GML-file validation * @param handler Validation handler for GML-file validation
* @param verbose If verbosity is enabled the parser will write info logs to the logger * @param verbose If verbosity is enabled the parser will write info logs to the logger
* @param nested If nested is enabled the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache} * @param unconnected If true the parser will use an {@link de.hft.stuttgart.citydoctor2.database.UnconnectedCache UnconnectedCache}
* to collect the parsed CityObjects
* @return A {@link CityDoctorModel} object with the parsed Features * @return A {@link CityDoctorModel} object with the parsed Features
* @throws CityGmlParseException if parsing of the CityGML-file failed * @throws CityGmlParseException if parsing of the CityGML-file failed
* @throws InvalidGmlFileException if the GMLValidationHandler found an error * @throws InvalidGmlFileException if the GMLValidationHandler found an error
*/ */
private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, private static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l,
GMLValidationHandler handler, boolean verbose, boolean nested) GMLValidationHandler handler, boolean verbose, boolean unconnected)
throws CityGmlParseException, InvalidGmlFileException { throws CityGmlParseException, InvalidGmlFileException {
CityGMLContext context = getContext(); CityGMLContext context = getContext();
Path file = Paths.get(filePath); Path file = Paths.get(filePath);
...@@ -279,7 +279,7 @@ public class CityGmlParser { ...@@ -279,7 +279,7 @@ public class CityGmlParser {
if (l != null) { if (l != null) {
ois.addListener(l::updateProgress); ois.addListener(l::updateProgress);
} }
return readAndKeepFeatures(config, file, in, ois, verbose, nested); return readAndKeepFeatures(config, file, in, ois, verbose, unconnected);
} }
} catch (CityGMLReadException | IOException e) { } catch (CityGMLReadException | IOException e) {
throw new CityGmlParseException("Failed to read CityGML file", e); throw new CityGmlParseException("Failed to read CityGML file", e);
...@@ -431,8 +431,8 @@ public class CityGmlParser { ...@@ -431,8 +431,8 @@ public class CityGmlParser {
private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file,
CityGMLInputFactory inputFactory, ObservedInputStream ois, CityGMLInputFactory inputFactory, ObservedInputStream ois,
boolean verbose, boolean nested) throws CityGMLReadException { boolean verbose, boolean unconnected) throws CityGMLReadException {
return readAndKeepModel(new Citygml3FeatureMapper(config, file, nested), inputFactory, ois, verbose); return readAndKeepModel(new Citygml3FeatureMapper(config, file, unconnected), inputFactory, ois, verbose);
} }
private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory,
...@@ -445,7 +445,8 @@ public class CityGmlParser { ...@@ -445,7 +445,8 @@ public class CityGmlParser {
List<AbstractCityObject> acos = new ArrayList<>(); List<AbstractCityObject> acos = new ArrayList<>();
CityModel cgmlModel = null; CityModel cgmlModel = null;
CityDoctorModel cdModel = mapper.getModel(); CityDoctorModel cdModel = mapper.getModel();
cdModel.getCache().resetCache(); AutoCloseable parsingContext = cdModel.getCache().startParsingMode();
try (parsingContext){
List<AbstractCityObject> queuedObjects = new ArrayList<>(); List<AbstractCityObject> queuedObjects = new ArrayList<>();
while (reader.hasNext()) { while (reader.hasNext()) {
AbstractFeature chunk = reader.next(); AbstractFeature chunk = reader.next();
...@@ -494,8 +495,11 @@ public class CityGmlParser { ...@@ -494,8 +495,11 @@ public class CityGmlParser {
mapper.getModel().getNumberOfFeatures()); mapper.getModel().getNumberOfFeatures());
} }
mapper.setCityGMLVersion(version); mapper.setCityGMLVersion(version);
CacheManager.finishParsing();
return mapper.getModel(); return mapper.getModel();
} catch (Exception e) {
throw new CityGMLReadException(e);
}
} }
} }
......
...@@ -5,15 +5,16 @@ import org.citygml4j.core.model.building.AbstractBuilding; ...@@ -5,15 +5,16 @@ import org.citygml4j.core.model.building.AbstractBuilding;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
public class FeatureCacheTest { public class FeatureCacheTest {
@Test @Test
public void testPut(){ public void testPut(){
FeatureCache cache = new FeatureCache(); FeatureCache cache = CacheManager.getFeatureCache();
Building b = new Building(); Building b = new Building();
b.setGmlObject(mock(AbstractBuilding.class)); b.setGmlObject(mock(AbstractBuilding.class));
b.setGmlId(new GmlId("test")); b.setGmlId(new GmlId("test"));
...@@ -21,18 +22,22 @@ public class FeatureCacheTest { ...@@ -21,18 +22,22 @@ public class FeatureCacheTest {
assertEquals(0, cache.getTotalFeatureCount()); assertEquals(0, cache.getTotalFeatureCount());
cache.put(b); cache.put(b);
cache.finishParsing();
assertEquals(1, cache.getTotalFeatureCount()); assertEquals(1, cache.getTotalFeatureCount());
} }
@Test @Test
public void testGet(){ public void testGet(){
FeatureCache cache = new FeatureCache(); FeatureCache cache = CacheManager.getFeatureCache();
EmbeddedDatabaseHandler handler = new EmbeddedDatabaseHandler(); assertNull(cache.get(new GmlId("building")));
Building b = new Building(); Building b = new Building();
b.setGmlId(new GmlId("building"));
b.setGmlObject(mock(AbstractBuilding.class));
b.setGmlId(new GmlId("test"));
b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0)); b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0));
handler.marshallCityObject(b); assertEquals(0, cache.getTotalFeatureCount());
cache.put(b);
CityObject b2 = cache.get(new GmlId("building")); CityObject b2 = cache.get(new GmlId("building"));
assertEquals(b.getGmlId(), b2.getGmlId()); assertEquals(b.getGmlId(), b2.getGmlId());
......
...@@ -193,13 +193,23 @@ public class Checker { ...@@ -193,13 +193,23 @@ public class Checker {
if (config == null) { if (config == null) {
config = ValidationConfiguration.loadStandardValidationConfig(); config = ValidationConfiguration.loadStandardValidationConfig();
} }
AutoCloseable checkingContext = model.getCache().startCheckingMode();
try(checkingContext){
Callable<SvrlContentHandler> schematronThread = () -> executeSchematronValidationIfAvailable(config, model.getFile()); Callable<SvrlContentHandler> schematronThread = () -> executeSchematronValidationIfAvailable(config, model.getFile());
ExecutorService executor = Executors.newSingleThreadExecutor(); ExecutorService executor = Executors.newSingleThreadExecutor();
Future<SvrlContentHandler> handlerFuture = executor.submit(schematronThread); Future<SvrlContentHandler> handlerFuture = executor.submit(schematronThread);
executor.shutdown(); executor.shutdown();
checkCityModel(model, l); checkCityModel(model, l);
getSchematronResults(handlerFuture, l);
model.setValidated(createValidationPlan());
} catch (Exception e){
logger.error("Validation failed due to encountering error", e);
}
}
private void getSchematronResults(Future<SvrlContentHandler> handlerFuture, ProgressListener l) {
if (!handlerFuture.isDone()) { if (!handlerFuture.isDone()) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(Localization.getText("Checker.schematronStillRunning")); logger.info(Localization.getText("Checker.schematronStillRunning"));
...@@ -219,8 +229,6 @@ public class Checker { ...@@ -219,8 +229,6 @@ public class Checker {
logger.error(Localization.getText("Checker.schematronInterrupted"), e); logger.error(Localization.getText("Checker.schematronInterrupted"), e);
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
CacheManager.synchronizeCache();
model.setValidated(createValidationPlan());
} }
private void handleSchematronResults(SvrlContentHandler handler) { private void handleSchematronResults(SvrlContentHandler handler) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment