Implemented simple caching for JsonPlantDatabase

Also added Javadoc for the PlantDatabase interface
This commit is contained in:
David Guler 2022-10-23 11:11:52 +02:00
parent 7355ce563f
commit 2c61cd3393
3 changed files with 91 additions and 15 deletions

View File

@ -0,0 +1,7 @@
package ch.zhaw.gartenverwaltung.io;
public class HardinessZoneNotSetException extends Exception {
public HardinessZoneNotSetException() {
super("HardinessZone must be set to retrieve plants!");
}
}

View File

@ -11,12 +11,25 @@ import java.net.URL;
import java.time.MonthDay; import java.time.MonthDay;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
/**
* Implements the {@link PlantDatabase} interface for loading {@link Plant} objects
* from a JSON file.
* The reads are cached to minimize file-io operations.
*/
public class JsonPlantDatabase implements PlantDatabase { public class JsonPlantDatabase implements PlantDatabase {
private final URL dataSource = getClass().getResource("plantdb.json"); private final URL dataSource = getClass().getResource("plantdb.json");
private HardinessZone currentZone;
private Map<Long, Plant> plantMap = Collections.emptyMap();
/**
* Creating constant objects required to deserialize the {@link MonthDay} classes
*/
private final static JavaTimeModule timeModule = new JavaTimeModule(); private final static JavaTimeModule timeModule = new JavaTimeModule();
static { static {
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd");
@ -24,27 +37,62 @@ public class JsonPlantDatabase implements PlantDatabase {
timeModule.addDeserializer(MonthDay.class, dateDeserializer); timeModule.addDeserializer(MonthDay.class, dateDeserializer);
} }
/**
* If no data is currently loaded, or the specified zone differs
* from the {@link #currentZone}, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #plantMap} are returned.
*
* @see PlantDatabase#getPlantList(HardinessZone)
*/
@Override @Override
public List<Plant> getPlantList(HardinessZone zone) throws IOException { public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException {
List<Plant> result = Collections.emptyList(); if (zone == null) {
throw new HardinessZoneNotSetException();
}
if (plantMap.isEmpty() || zone != currentZone) {
loadPlantList(zone);
}
return plantMap.values().stream().toList();
}
/**
* @see PlantDatabase#getPlantById(long)
*/
@Override
public Optional<Plant> getPlantById(long id) throws HardinessZoneNotSetException, IOException {
if (currentZone == null) {
throw new HardinessZoneNotSetException();
}
if (plantMap.isEmpty()) {
loadPlantList(currentZone);
}
return Optional.ofNullable(plantMap.get(id));
}
/**
* Loads the database from {@link #dataSource} and updates the cached data.
*
* @param zone The {@link HardinessZone} for which data is to be loaded
* @throws IOException If the database cannot be accessed
*/
private void loadPlantList(HardinessZone zone) throws IOException {
if (dataSource != null) { if (dataSource != null) {
currentZone = zone;
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule); mapper.registerModule(timeModule);
List<Plant> result;
result = mapper.readerForListOf(Plant.class).readValue(dataSource); result = mapper.readerForListOf(Plant.class).readValue(dataSource);
}
for (Plant plant : result) {
plant.inZone(zone);
}
return result; for (Plant plant : result) {
} plant.inZone(currentZone);
}
@Override // Turn list into a HashMap with structure id => Plant
public Optional<Plant> getPlantById(long id, HardinessZone zone) throws IOException { plantMap = result.stream()
return getPlantList(zone).stream() .collect(HashMap::new,
.filter(plant -> plant.id() != id) (res, plant) -> res.put(plant.id(), plant),
.findFirst(); (existing, replacement) -> { });
}
} }
} }

View File

@ -7,7 +7,28 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
/**
* A database of {@link Plant}s.
* The interface specifies the minimal required operations.
*/
public interface PlantDatabase { public interface PlantDatabase {
List<Plant> getPlantList(HardinessZone zone) throws IOException; /**
Optional<Plant> getPlantById(long id, HardinessZone zone) throws IOException; * Yields a list of all {@link Plant}s in the database with only data relevant to the specfied {@link HardinessZone}
*
* @param zone The zone for which data should be fetched
* @return A list of {@link Plant}s with data for the specified zone
* @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
*/
List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException;
/**
* Attempts to retrieve the {@link Plant} with the specified id.
*
* @param id The {@link Plant#id()} to look for
* @return {@link Optional} of the found {@link Plant}, {@link Optional#empty()} if no entry matched the criteria
* @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
*/
Optional<Plant> getPlantById(long id) throws IOException, HardinessZoneNotSetException;
} }