From df746703fac9c5d8cebd9da3f4ee9c9987fe301a Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 24 Apr 2023 13:45:16 +0200 Subject: [PATCH 01/11] Make ``handleEntity`` throw exceptions instead of returning empty optional. --- .../io/processor/EntityProcessor.java | 6 +- .../io/processor/ProcessorProvider.java | 14 +-- .../timeseries/TimeSeriesProcessor.java | 2 +- .../ie3/datamodel/io/sink/CsvFileSink.java | 34 +------- .../ie3/datamodel/io/sink/InfluxDbSink.java | 67 +++++++-------- .../io/processor/ProcessorProviderTest.groovy | 21 ++--- .../input/InputEntityProcessorTest.groovy | 86 +++++++------------ .../result/ResultEntityProcessorTest.groovy | 15 ++-- 8 files changed, 94 insertions(+), 151 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java index 3209799f9..ccac990a2 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java @@ -51,7 +51,7 @@ protected EntityProcessor(Class registeredClass) { * @return an optional Map with fieldName to fieldValue or an empty optional if an error occurred * during processing */ - public Optional> handleEntity(T entity) { + public LinkedHashMap handleEntity(T entity) { if (!registeredClass.equals(entity.getClass())) throw new EntityProcessorException( "Cannot process " @@ -63,10 +63,10 @@ public Optional> handleEntity(T entity) { + ".class!"); try { - return Optional.of(processObject(entity, fieldNameToMethod)); + return processObject(entity, fieldNameToMethod); } catch (EntityProcessorException e) { logger.error("Cannot process the entity{}.", entity, e); - return Optional.empty(); + throw new EntityProcessorException("Entity " + entity + " cannot be processed.", e); } } diff --git a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java index 8539ca52b..d36d43ff0 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java @@ -70,13 +70,14 @@ public ProcessorProvider( this.timeSeriesProcessors = timeSeriesProcessors; } - public Optional> handleEntity(T entity) { + public LinkedHashMap handleEntity(T entity) + throws ProcessorProviderException { try { EntityProcessor processor = getEntityProcessor(entity.getClass()); return castProcessor(processor).handleEntity(entity); } catch (ProcessorProviderException e) { log.error("Exception occurred during entity handling.", e); - return Optional.empty(); + throw e; } } @@ -112,17 +113,18 @@ private EntityProcessor getEntityProcessor( * @return A set of mappings from field name to value */ public , E extends TimeSeriesEntry, V extends Value> - Optional>> handleTimeSeries(T timeSeries) { + Set> handleTimeSeries(T timeSeries) + throws ProcessorProviderException { TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); try { TimeSeriesProcessor processor = getTimeSeriesProcessor(key); - return Optional.of(processor.handleTimeSeries(timeSeries)); + return processor.handleTimeSeries(timeSeries); } catch (ProcessorProviderException e) { log.error("Cannot handle the time series '{}'.", timeSeries, e); - return Optional.empty(); + throw e; } catch (EntityProcessorException e) { log.error("Error during processing of time series.", e); - return Optional.empty(); + throw e; } } diff --git a/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java index e4b9dbcfa..7daabe432 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java @@ -176,7 +176,7 @@ private SortedMap buildFieldToSource( } @Override - public Optional> handleEntity(TimeSeries entity) { + public LinkedHashMap handleEntity(TimeSeries entity) { throw new UnsupportedOperationException( "Don't invoke this simple method, but TimeSeriesProcessor#handleTimeSeries(TimeSeries)."); } diff --git a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java index ec50a044f..2eaf3b08f 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java @@ -277,22 +277,9 @@ public , V extends Value> void persistTimeSeries( private , V extends Value> void persistTimeSeries( TimeSeries timeSeries, BufferedCsvWriter writer) { - TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); - try { Set> entityFieldData = - processorProvider - .handleTimeSeries(timeSeries) - .orElseThrow( - () -> - new SinkException( - "Cannot persist time series of combination '" - + key - + "'. This sink can only process the following combinations: [" - + processorProvider.getRegisteredTimeSeriesCombinations().stream() - .map(TimeSeriesProcessorKey::toString) - .collect(Collectors.joining(",")) - + "]")); + processorProvider.handleTimeSeries(timeSeries); entityFieldData.forEach( data -> { try { @@ -303,8 +290,9 @@ private , V extends Value> void persistTimeSeries( log.error("Exception occurred during processing the provided data fields: ", e); } }); - } catch (SinkException e) { + } catch (ProcessorProviderException e) { log.error("Exception occurred during processor request: ", e); + throw new RuntimeException(e); } } @@ -318,21 +306,7 @@ private , V extends Value> void persistTimeSeries( private void write(C entity) { LinkedHashMap entityFieldData; try { - entityFieldData = - processorProvider - .handleEntity(entity) - .map(this::csvEntityFieldData) - .orElseThrow( - () -> - new SinkException( - "Cannot persist entity of type '" - + entity.getClass().getSimpleName() - + "'. This sink can only process the following entities: [" - + processorProvider.getRegisteredClasses().stream() - .map(Class::getSimpleName) - .collect(Collectors.joining(",")) - + "]")); - + entityFieldData = csvEntityFieldData(processorProvider.handleEntity(entity)); String[] headerElements = processorProvider.getHeaderElements(entity.getClass()); BufferedCsvWriter writer = connector.getOrInitWriter(entity.getClass(), headerElements, csvSep); diff --git a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java index 20eac6dbb..ca9b3bc29 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java @@ -5,6 +5,7 @@ */ package edu.ie3.datamodel.io.sink; +import edu.ie3.datamodel.exceptions.ProcessorProviderException; import edu.ie3.datamodel.exceptions.SinkException; import edu.ie3.datamodel.io.connectors.InfluxDbConnector; import edu.ie3.datamodel.io.naming.EntityPersistenceNamingStrategy; @@ -127,19 +128,7 @@ private Optional transformToPoint(ResultEntity entity) { private Optional transformToPoint(ResultEntity entity, String measurementName) { LinkedHashMap entityFieldData; try { - entityFieldData = - processorProvider - .handleEntity(entity) - .orElseThrow( - () -> - new SinkException( - "Cannot persist entity of type '" - + entity.getClass().getSimpleName() - + "'. This sink can only process the following entities: [" - + processorProvider.getRegisteredClasses().stream() - .map(Class::getSimpleName) - .collect(Collectors.joining(",")) - + "]")); + entityFieldData = processorProvider.handleEntity(entity); entityFieldData.remove(FIELD_NAME_TIME); return Optional.of( Point.measurement(transformToMeasurementName(measurementName)) @@ -148,11 +137,18 @@ private Optional transformToPoint(ResultEntity entity, String measurement .tag("scenario", connector.getScenarioName()) .fields(Collections.unmodifiableMap(entityFieldData)) .build()); - } catch (SinkException e) { + } catch (ProcessorProviderException e) { log.error( "Cannot persist provided entity '{}'. Exception: {}", entity.getClass().getSimpleName(), - e); + new SinkException( + "Cannot persist entity of type '" + + entity.getClass().getSimpleName() + + "'. This sink can only process the following entities: [" + + processorProvider.getRegisteredClasses().stream() + .map(Class::getSimpleName) + .collect(Collectors.joining(",")) + + "]")); } return Optional.empty(); } @@ -168,16 +164,21 @@ private Optional transformToPoint(ResultEntity entity, String measurement private , V extends Value> Set transformToPoints( TimeSeries timeSeries) { if (timeSeries.getEntries().isEmpty()) return Collections.emptySet(); - Optional measurementName = entityPersistenceNamingStrategy.getEntityName(timeSeries); - if (measurementName.isEmpty()) { - String valueClassName = - timeSeries.getEntries().iterator().next().getValue().getClass().getSimpleName(); - log.warn( - "I could not get a measurement name for TimeSeries value class {}. I am using it's value's simple name instead.", - valueClassName); - return transformToPoints(timeSeries, valueClassName); + + try { + Optional measurementName = entityPersistenceNamingStrategy.getEntityName(timeSeries); + if (measurementName.isEmpty()) { + String valueClassName = + timeSeries.getEntries().iterator().next().getValue().getClass().getSimpleName(); + log.warn( + "I could not get a measurement name for TimeSeries value class {}. I am using it's value's simple name instead.", + valueClassName); + return transformToPoints(timeSeries, valueClassName); + } + return transformToPoints(timeSeries, measurementName.get()); + } catch (ProcessorProviderException e) { + throw new RuntimeException(e); } - return transformToPoints(timeSeries, measurementName.get()); } /** @@ -188,23 +189,12 @@ private , V extends Value> Set transformToPo * @param measurementName equivalent to the name of a relational table */ private , V extends Value> Set transformToPoints( - TimeSeries timeSeries, String measurementName) { + TimeSeries timeSeries, String measurementName) throws ProcessorProviderException { TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); Set points = new HashSet<>(); try { Set> entityFieldData = - processorProvider - .handleTimeSeries(timeSeries) - .orElseThrow( - () -> - new SinkException( - "Cannot persist time series of combination '" - + key - + "'. This sink can only process the following combinations: [" - + processorProvider.getRegisteredTimeSeriesCombinations().stream() - .map(TimeSeriesProcessorKey::toString) - .collect(Collectors.joining(",")) - + "]")); + processorProvider.handleTimeSeries(timeSeries); for (LinkedHashMap dataMapping : entityFieldData) { String timeString = dataMapping.remove(FIELD_NAME_TIME); @@ -217,8 +207,9 @@ private , V extends Value> Set transformToPo .build(); points.add(point); } - } catch (SinkException e) { + } catch (ProcessorProviderException e) { log.error("Cannot persist provided time series '{}'. Exception: {}", key, e); + throw e; } return points; } diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy index 32a6279b4..1519db1fc 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy @@ -224,19 +224,19 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData PvResult pvResult = new PvResult(uuid, TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"), inputModel, p, q) and: - Optional processorResult = provider.handleEntity(pvResult) + LinkedHashMap resultMap = provider.handleEntity(pvResult) then: - processorResult.present - Map resultMap = processorResult.get() resultMap.size() == 5 resultMap == expectedMap when: - Optional result = provider.handleEntity(new WecResult(uuid, TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"), inputModel, p, q)) + provider.handleEntity(new WecResult(uuid, TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"), inputModel, p, q)) then: - !result.present + Exception ex = thrown() + ex.class == ProcessorProviderException + ex.message == "Cannot find a suitable processor for provided class with name 'WecResult'. This provider's processors can process: PvResult,EvResult" } def "A ProcessorProvider returns an empty Optional, if none of the assigned processors is able to handle a time series"() { @@ -248,10 +248,12 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData ProcessorProvider provider = new ProcessorProvider([], timeSeriesProcessorMap) when: - Optional>> actual = provider.handleTimeSeries(individualIntTimeSeries) + Set> actual = provider.handleTimeSeries(individualIntTimeSeries) then: - !actual.present + Exception ex = thrown() + ex.class == ProcessorProviderException + ex.message == "Cannot find processor for time series combination 'TimeSeriesProcessorKey{timeSeriesClass=class edu.ie3.datamodel.models.timeseries.individual.IndividualTimeSeries, entryClass=class edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue, valueClass=class edu.ie3.datamodel.models.timeseries.IntValue}'. Either your provider is not properly initialized or there is no implementation to process this entity class!)" } def "A ProcessorProvider handles a time series correctly"() { @@ -263,10 +265,9 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData ProcessorProvider provider = new ProcessorProvider([], timeSeriesProcessorMap) when: - Optional>> actual = provider.handleTimeSeries(individualEnergyPriceTimeSeries) + Set> actual = provider.handleTimeSeries(individualEnergyPriceTimeSeries) then: - actual.present - actual.get() == individualEnergyPriceTimeSeriesProcessed + actual == individualEnergyPriceTimeSeriesProcessed } } diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy index defe04b4d..aef13bce0 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy @@ -63,8 +63,7 @@ class InputEntityProcessorTest extends Specification { def processingResult = processor.handleEntity(validResult) then: "make sure that the result is as expected " - processingResult.present - processingResult.get() == expectedResults + processingResult == expectedResults } def "A InputEntityProcessor should serialize a provided ConnectorInput correctly"() { @@ -76,9 +75,7 @@ class InputEntityProcessorTest extends Specification { def processingResult = processor.handleEntity(validInput) then: "make sure that the result is as expected " - processingResult.present - - processingResult.get() == expectedResult + processingResult == expectedResult where: modelClass | modelInstance || expectedResult @@ -146,9 +143,7 @@ class InputEntityProcessorTest extends Specification { def processingResult = processor.handleEntity(validInput) then: "make sure that the result is as expected " - processingResult.present - - processingResult.get().forEach { k, v -> + processingResult.forEach { k, v -> if (k != "nodeInternal") // the internal 3w node is always randomly generated, hence we can skip to test on this assert (v == expectedResult.get(k)) } @@ -296,11 +291,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(validNode) + LinkedHashMap actual = processor.handleEntity(validNode) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided NodeGraphicInput with path correctly"() { @@ -316,11 +310,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(validNode) + LinkedHashMap actual = processor.handleEntity(validNode) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided LineGraphicInput correctly"() { @@ -335,11 +328,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(validNode) + LinkedHashMap actual = processor.handleEntity(validNode) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided OperatorInput correctly"() { @@ -352,11 +344,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(operator) + LinkedHashMap actual = processor.handleEntity(operator) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided RandomLoadParameters correctly"() { @@ -390,11 +381,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(parameters) + LinkedHashMap actual = processor.handleEntity(parameters) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided WecTypeInput correctly"() { @@ -415,11 +405,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided Transformer2WTypeInput correctly"() { @@ -445,11 +434,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided Transformer3WTypeInput correctly"() { @@ -481,11 +469,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided LineTypeInput correctly"() { @@ -504,11 +491,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided EvTypeInput correctly"() { @@ -527,11 +513,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided ChpTypeInput correctly"() { @@ -552,11 +537,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided HpTypeInput correctly"() { @@ -574,11 +558,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided BmTypeInput correctly"() { @@ -597,11 +580,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize a provided StorageTypeInput correctly"() { @@ -625,11 +607,10 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(type) + Map actual = processor.handleEntity(type) then: - actual.present - actual.get() == expected + actual == expected } def "The InputEntityProcessor should serialize an entity but ignore the operator field when OperatorInput is equal to NO_OPERATOR_ASSIGNED"() { @@ -660,10 +641,9 @@ class InputEntityProcessorTest extends Specification { ] when: - Optional> actual = processor.handleEntity(nodeWithOutOperator) + Map actual = processor.handleEntity(nodeWithOutOperator) then: - actual.present - actual.get() == expected + actual == expected } } diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/result/ResultEntityProcessorTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/result/ResultEntityProcessorTest.groovy index d634f8c96..94f55d0ad 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/result/ResultEntityProcessorTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/result/ResultEntityProcessorTest.groovy @@ -73,8 +73,7 @@ class ResultEntityProcessorTest extends Specification { def validProcessedElement = sysPartResProcessor.handleEntity(validResult) then: - validProcessedElement.present - validProcessedElement.get() == expectedResults + validProcessedElement == expectedResults where: modelClass | validSystemParticipantResult || expectedResults @@ -123,8 +122,7 @@ class ResultEntityProcessorTest extends Specification { def validProcessedElement = sysPartResProcessor.handleEntity(validResult) then: - validProcessedElement.present - validProcessedElement.get() == expectedResults + validProcessedElement == expectedResults } def "A ResultEntityProcessor should serialize a FlexOptionsResult correctly"() { @@ -151,8 +149,7 @@ class ResultEntityProcessorTest extends Specification { def validProcessedElement = sysPartResProcessor.handleEntity(validResult) then: - validProcessedElement.present - validProcessedElement.get() == expectedResults + validProcessedElement == expectedResults } @Shared @@ -226,8 +223,7 @@ class ResultEntityProcessorTest extends Specification { def validProcessedElement = sysPartResProcessor.handleEntity(validResult) then: - validProcessedElement.present - validProcessedElement.get() == expectedResults + validProcessedElement == expectedResults where: modelClass | validConnectorResult || expectedResults @@ -258,8 +254,7 @@ class ResultEntityProcessorTest extends Specification { def validProcessedElement = sysPartResProcessor.handleEntity(validResult) then: - validProcessedElement.present - validProcessedElement.get() == expectedResults + validProcessedElement == expectedResults } def "A ResultEntityProcessor should throw an EntityProcessorException when it receives an entity result that is not eligible"() { From 1a207d766e4e49f78b7538beaad36df60babaccb Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 24 Apr 2023 13:55:47 +0200 Subject: [PATCH 02/11] Fix failing test. --- .../ie3/datamodel/io/processor/ProcessorProviderTest.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy index 1519db1fc..bc18ba92e 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy @@ -236,7 +236,8 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData then: Exception ex = thrown() ex.class == ProcessorProviderException - ex.message == "Cannot find a suitable processor for provided class with name 'WecResult'. This provider's processors can process: PvResult,EvResult" + List.of("Cannot find a suitable processor for provided class with name 'WecResult'. This provider's processors can process: ","PvResult", "EvResult") + .forEach {str -> ex.message.contains(str)} } def "A ProcessorProvider returns an empty Optional, if none of the assigned processors is able to handle a time series"() { @@ -248,7 +249,7 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData ProcessorProvider provider = new ProcessorProvider([], timeSeriesProcessorMap) when: - Set> actual = provider.handleTimeSeries(individualIntTimeSeries) + provider.handleTimeSeries(individualIntTimeSeries) then: Exception ex = thrown() From 546edb42e574e57a5b3bca454aa5ff6d989babda Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 26 Apr 2023 11:05:35 +0200 Subject: [PATCH 03/11] Fix quality gate issues. --- .../ie3/datamodel/io/sink/CsvFileSink.java | 4 +-- .../edu/ie3/datamodel/io/sink/DataSink.java | 8 +++--- .../ie3/datamodel/io/sink/InfluxDbSink.java | 25 ++++++++++++------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java index 2eaf3b08f..56ad70ad5 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java @@ -276,7 +276,7 @@ public , V extends Value> void persistTimeSeries( } private , V extends Value> void persistTimeSeries( - TimeSeries timeSeries, BufferedCsvWriter writer) { + TimeSeries timeSeries, BufferedCsvWriter writer) throws ProcessorProviderException { try { Set> entityFieldData = processorProvider.handleTimeSeries(timeSeries); @@ -292,7 +292,7 @@ private , V extends Value> void persistTimeSeries( }); } catch (ProcessorProviderException e) { log.error("Exception occurred during processor request: ", e); - throw new RuntimeException(e); + throw e; } } diff --git a/src/main/java/edu/ie3/datamodel/io/sink/DataSink.java b/src/main/java/edu/ie3/datamodel/io/sink/DataSink.java index f5a20b9b2..4809b80fe 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/DataSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/DataSink.java @@ -5,6 +5,7 @@ */ package edu.ie3.datamodel.io.sink; +import edu.ie3.datamodel.exceptions.ProcessorProviderException; import edu.ie3.datamodel.io.connectors.DataConnector; import edu.ie3.datamodel.io.processor.EntityProcessor; import edu.ie3.datamodel.models.UniqueEntity; @@ -37,7 +38,7 @@ public interface DataSink { * @param bounded to be all unique entities. Handling of specific entities is normally then * executed by a specific {@link EntityProcessor} */ - void persist(C entity); + void persist(C entity) throws ProcessorProviderException; /** * Should implement the entry point of a data sink to persist multiple entities in a collection. @@ -51,7 +52,8 @@ public interface DataSink { * @param bounded to be all unique entities. Handling of specific entities is normally then * executed by a specific {@link EntityProcessor} */ - void persistAll(Collection entities); + void persistAll(Collection entities) + throws ProcessorProviderException; /** * Should implement the handling of a whole time series. Therefore the single entries have to be @@ -62,5 +64,5 @@ public interface DataSink { * @param Type of actual value, that is inside the entry */ , V extends Value> void persistTimeSeries( - TimeSeries timeSeries); + TimeSeries timeSeries) throws ProcessorProviderException; } diff --git a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java index ca9b3bc29..8fd4124e1 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java @@ -10,7 +10,6 @@ import edu.ie3.datamodel.io.connectors.InfluxDbConnector; import edu.ie3.datamodel.io.naming.EntityPersistenceNamingStrategy; import edu.ie3.datamodel.io.processor.ProcessorProvider; -import edu.ie3.datamodel.io.processor.timeseries.TimeSeriesProcessorKey; import edu.ie3.datamodel.models.UniqueEntity; import edu.ie3.datamodel.models.result.ResultEntity; import edu.ie3.datamodel.models.timeseries.TimeSeries; @@ -69,7 +68,7 @@ public void shutdown() { } @Override - public void persist(C entity) { + public void persist(C entity) throws ProcessorProviderException { Set points = extractPoints(entity); // writes only the exact one point instead of unnecessarily wrapping it in BatchPoints if (points.size() == 1) write(points.iterator().next()); @@ -77,7 +76,8 @@ public void persist(C entity) { } @Override - public void persistAll(Collection entities) { + public void persistAll(Collection entities) + throws ProcessorProviderException { Set points = new HashSet<>(); for (C entity : entities) { points.addAll(extractPoints(entity)); @@ -87,7 +87,7 @@ public void persistAll(Collection entities) { @Override public , V extends Value> void persistTimeSeries( - TimeSeries timeSeries) { + TimeSeries timeSeries) throws ProcessorProviderException { Set points = transformToPoints(timeSeries); writeAll(points); } @@ -162,7 +162,7 @@ private Optional transformToPoint(ResultEntity entity, String measurement * @param timeSeries the time series to transform */ private , V extends Value> Set transformToPoints( - TimeSeries timeSeries) { + TimeSeries timeSeries) throws ProcessorProviderException { if (timeSeries.getEntries().isEmpty()) return Collections.emptySet(); try { @@ -177,7 +177,11 @@ private , V extends Value> Set transformToPo } return transformToPoints(timeSeries, measurementName.get()); } catch (ProcessorProviderException e) { - throw new RuntimeException(e); + log.error( + "Cannot persist provided time series '{}'. Exception: {}", + timeSeries.getClass().getSimpleName(), + e); + throw e; } } @@ -190,7 +194,6 @@ private , V extends Value> Set transformToPo */ private , V extends Value> Set transformToPoints( TimeSeries timeSeries, String measurementName) throws ProcessorProviderException { - TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); Set points = new HashSet<>(); try { Set> entityFieldData = @@ -208,7 +211,10 @@ private , V extends Value> Set transformToPo points.add(point); } } catch (ProcessorProviderException e) { - log.error("Cannot persist provided time series '{}'. Exception: {}", key, e); + log.error( + "Cannot persist provided entity '{}'. Exception: {}", + timeSeries.getClass().getSimpleName(), + e); throw e; } return points; @@ -224,7 +230,8 @@ private , V extends Value> Set transformToPo * @param bounded to be all unique entities, but logs an error and returns an empty Set if it * does not extend {@link ResultEntity} or {@link TimeSeries} */ - private Set extractPoints(C entity) { + private Set extractPoints(C entity) + throws ProcessorProviderException { Set points = new HashSet<>(); /* Distinguish between result models and time series */ if (entity instanceof ResultEntity resultEntity) { From f392485db6cb87dff1ce3d9f7a62dc5b48db516d Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 26 Apr 2023 13:16:38 +0200 Subject: [PATCH 04/11] Fix quality gate issues. --- .../io/processor/ProcessorProvider.java | 5 ++- .../ie3/datamodel/io/sink/CsvFileSink.java | 2 +- .../ie3/datamodel/io/sink/InfluxDbSink.java | 44 ++++++++----------- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java index d36d43ff0..2230bd43e 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java @@ -121,10 +121,11 @@ Set> handleTimeSeries(T timeSeries) return processor.handleTimeSeries(timeSeries); } catch (ProcessorProviderException e) { log.error("Cannot handle the time series '{}'.", timeSeries, e); - throw e; + throw new ProcessorProviderException( + "Cannot handle the time series {" + timeSeries + "}.", e); } catch (EntityProcessorException e) { log.error("Error during processing of time series.", e); - throw e; + throw new EntityProcessorException("Error during processing of time series.", e); } } diff --git a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java index 56ad70ad5..b90aa0b0b 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java @@ -292,7 +292,7 @@ private , V extends Value> void persistTimeSeries( }); } catch (ProcessorProviderException e) { log.error("Exception occurred during processor request: ", e); - throw e; + throw new ProcessorProviderException("Exception occurred during processor request: ", e); } } diff --git a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java index 8fd4124e1..0555f2ffd 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java @@ -6,7 +6,6 @@ package edu.ie3.datamodel.io.sink; import edu.ie3.datamodel.exceptions.ProcessorProviderException; -import edu.ie3.datamodel.exceptions.SinkException; import edu.ie3.datamodel.io.connectors.InfluxDbConnector; import edu.ie3.datamodel.io.naming.EntityPersistenceNamingStrategy; import edu.ie3.datamodel.io.processor.ProcessorProvider; @@ -18,7 +17,6 @@ import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import org.influxdb.dto.BatchPoints; import org.influxdb.dto.Point; import org.slf4j.Logger; @@ -108,7 +106,7 @@ public void flush() { * * @param entity the entity to transform */ - private Optional transformToPoint(ResultEntity entity) { + private Point transformToPoint(ResultEntity entity) throws ProcessorProviderException { Optional measurementName = entityPersistenceNamingStrategy.getResultEntityName(entity.getClass()); if (measurementName.isEmpty()) @@ -125,32 +123,26 @@ private Optional transformToPoint(ResultEntity entity) { * @param entity the entity to transform * @param measurementName equivalent to the name of a relational table */ - private Optional transformToPoint(ResultEntity entity, String measurementName) { + private Point transformToPoint(ResultEntity entity, String measurementName) + throws ProcessorProviderException { LinkedHashMap entityFieldData; try { entityFieldData = processorProvider.handleEntity(entity); entityFieldData.remove(FIELD_NAME_TIME); - return Optional.of( - Point.measurement(transformToMeasurementName(measurementName)) - .time(entity.getTime().toInstant().toEpochMilli(), TimeUnit.MILLISECONDS) - .tag("input_model", entityFieldData.remove(FIELD_NAME_INPUT)) - .tag("scenario", connector.getScenarioName()) - .fields(Collections.unmodifiableMap(entityFieldData)) - .build()); + return Point.measurement(transformToMeasurementName(measurementName)) + .time(entity.getTime().toInstant().toEpochMilli(), TimeUnit.MILLISECONDS) + .tag("input_model", entityFieldData.remove(FIELD_NAME_INPUT)) + .tag("scenario", connector.getScenarioName()) + .fields(Collections.unmodifiableMap(entityFieldData)) + .build(); } catch (ProcessorProviderException e) { log.error( "Cannot persist provided entity '{}'. Exception: {}", entity.getClass().getSimpleName(), - new SinkException( - "Cannot persist entity of type '" - + entity.getClass().getSimpleName() - + "'. This sink can only process the following entities: [" - + processorProvider.getRegisteredClasses().stream() - .map(Class::getSimpleName) - .collect(Collectors.joining(",")) - + "]")); + e); + + throw new ProcessorProviderException(e); } - return Optional.empty(); } /** @@ -181,7 +173,8 @@ private , V extends Value> Set transformToPo "Cannot persist provided time series '{}'. Exception: {}", timeSeries.getClass().getSimpleName(), e); - throw e; + throw new ProcessorProviderException( + "Cannot persist provided time series {" + timeSeries.getClass().getSimpleName() + "}", e); } } @@ -215,7 +208,8 @@ private , V extends Value> Set transformToPo "Cannot persist provided entity '{}'. Exception: {}", timeSeries.getClass().getSimpleName(), e); - throw e; + throw new ProcessorProviderException( + "Cannot persist provided time series {" + timeSeries.getClass().getSimpleName() + "}", e); } return points; } @@ -236,10 +230,8 @@ private Set extractPoints(C entity) /* Distinguish between result models and time series */ if (entity instanceof ResultEntity resultEntity) { try { - points.add( - transformToPoint(resultEntity) - .orElseThrow(() -> new SinkException("Could not transform entity"))); - } catch (SinkException e) { + points.add(transformToPoint(resultEntity)); + } catch (ProcessorProviderException e) { log.error( "Cannot persist provided entity '{}'. Exception: {}", entity.getClass().getSimpleName(), From ce12aa0a4d211ff42c90e0a20e6ba047a2e2c1ce Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 26 Apr 2023 13:36:41 +0200 Subject: [PATCH 05/11] Fixing failing test. --- .../ie3/datamodel/io/processor/ProcessorProviderTest.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy index bc18ba92e..7948503ce 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy @@ -254,7 +254,8 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData then: Exception ex = thrown() ex.class == ProcessorProviderException - ex.message == "Cannot find processor for time series combination 'TimeSeriesProcessorKey{timeSeriesClass=class edu.ie3.datamodel.models.timeseries.individual.IndividualTimeSeries, entryClass=class edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue, valueClass=class edu.ie3.datamodel.models.timeseries.IntValue}'. Either your provider is not properly initialized or there is no implementation to process this entity class!)" + ex.message == "Cannot handle the time series {" + individualIntTimeSeries + "}." + ex.cause.message == "Cannot find processor for time series combination 'TimeSeriesProcessorKey{timeSeriesClass=class edu.ie3.datamodel.models.timeseries.individual.IndividualTimeSeries, entryClass=class edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue, valueClass=class edu.ie3.datamodel.models.timeseries.IntValue}'. Either your provider is not properly initialized or there is no implementation to process this entity class!)" } def "A ProcessorProvider handles a time series correctly"() { From 0cb9fb6bc5cd083d45a74c1f2e1df82d97a1273a Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 26 Apr 2023 13:48:48 +0200 Subject: [PATCH 06/11] Fixing failing quality gate. --- .../ie3/datamodel/io/sink/InfluxDbSink.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java index 0555f2ffd..b57d04d8b 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java @@ -30,6 +30,8 @@ public class InfluxDbSink implements OutputDataSink { /** Field name for input model uuid field in result entities */ private static final String FIELD_NAME_INPUT = "inputModel"; + private static final String ERROR_MESSAGE = "Cannot persist provided entity '{}'. Exception: {}"; + private final InfluxDbConnector connector; private final EntityPersistenceNamingStrategy entityPersistenceNamingStrategy; private final ProcessorProvider processorProvider; @@ -136,10 +138,7 @@ private Point transformToPoint(ResultEntity entity, String measurementName) .fields(Collections.unmodifiableMap(entityFieldData)) .build(); } catch (ProcessorProviderException e) { - log.error( - "Cannot persist provided entity '{}'. Exception: {}", - entity.getClass().getSimpleName(), - e); + log.error(ERROR_MESSAGE, entity.getClass().getSimpleName(), e); throw new ProcessorProviderException(e); } @@ -169,10 +168,7 @@ private , V extends Value> Set transformToPo } return transformToPoints(timeSeries, measurementName.get()); } catch (ProcessorProviderException e) { - log.error( - "Cannot persist provided time series '{}'. Exception: {}", - timeSeries.getClass().getSimpleName(), - e); + log.error(ERROR_MESSAGE, timeSeries.getClass().getSimpleName(), e); throw new ProcessorProviderException( "Cannot persist provided time series {" + timeSeries.getClass().getSimpleName() + "}", e); } @@ -204,10 +200,7 @@ private , V extends Value> Set transformToPo points.add(point); } } catch (ProcessorProviderException e) { - log.error( - "Cannot persist provided entity '{}'. Exception: {}", - timeSeries.getClass().getSimpleName(), - e); + log.error(ERROR_MESSAGE, timeSeries.getClass().getSimpleName(), e); throw new ProcessorProviderException( "Cannot persist provided time series {" + timeSeries.getClass().getSimpleName() + "}", e); } @@ -232,10 +225,7 @@ private Set extractPoints(C entity) try { points.add(transformToPoint(resultEntity)); } catch (ProcessorProviderException e) { - log.error( - "Cannot persist provided entity '{}'. Exception: {}", - entity.getClass().getSimpleName(), - e); + log.error(ERROR_MESSAGE, entity.getClass().getSimpleName(), e); } } else if (entity instanceof TimeSeries timeSeries) { points.addAll(transformToPoints(timeSeries)); From bb64244a4ad3030c52ae9636535d44aa39ea3af3 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Tue, 1 Aug 2023 09:29:11 +0200 Subject: [PATCH 07/11] Implementing requested changes. --- .../exceptions/EntityProcessorException.java | 2 +- .../edu/ie3/datamodel/io/factory/Factory.java | 5 +- .../io/processor/EntityProcessor.java | 42 +++++---- .../ie3/datamodel/io/processor/Processor.java | 73 ++++++++------ .../io/processor/ProcessorProvider.java | 87 +++++++++-------- .../processor/input/InputEntityProcessor.java | 4 +- .../result/ResultEntityProcessor.java | 36 ++++--- .../timeseries/TimeSeriesProcessor.java | 11 ++- .../ie3/datamodel/io/sink/CsvFileSink.java | 14 ++- .../ie3/datamodel/io/sink/InfluxDbSink.java | 94 +++++++------------ .../java/edu/ie3/datamodel/utils/Try.java | 12 +++ .../io/processor/ProcessorProviderTest.groovy | 25 +++-- .../input/InputEntityProcessorTest.groovy | 10 +- 13 files changed, 224 insertions(+), 191 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/exceptions/EntityProcessorException.java b/src/main/java/edu/ie3/datamodel/exceptions/EntityProcessorException.java index f81118f49..37736b231 100644 --- a/src/main/java/edu/ie3/datamodel/exceptions/EntityProcessorException.java +++ b/src/main/java/edu/ie3/datamodel/exceptions/EntityProcessorException.java @@ -9,7 +9,7 @@ * Is thrown, when an something went wrong during entity field mapping creation in a {@link * edu.ie3.datamodel.io.processor.EntityProcessor} */ -public class EntityProcessorException extends RuntimeException { +public class EntityProcessorException extends Exception { public EntityProcessorException(final String message, final Throwable cause) { super(message, cause); } diff --git a/src/main/java/edu/ie3/datamodel/io/factory/Factory.java b/src/main/java/edu/ie3/datamodel/io/factory/Factory.java index 759fa9e4d..0428701ec 100644 --- a/src/main/java/edu/ie3/datamodel/io/factory/Factory.java +++ b/src/main/java/edu/ie3/datamodel/io/factory/Factory.java @@ -28,6 +28,7 @@ public abstract class Factory { private final List> supportedClasses; + @SafeVarargs protected Factory(Class... supportedClasses) { this.supportedClasses = Arrays.asList(supportedClasses); } @@ -54,9 +55,9 @@ public Try get(D data) { validateParameters(data, allFields.toArray((IntFunction[]>) Set[]::new)); // build the model - return new Success<>(buildModel(data)); + return Success.of(buildModel(data)); } catch (FactoryException e) { - return new Failure<>( + return Failure.of( new FactoryException( "An error occurred when creating instance of " + data.getTargetClass().getSimpleName() diff --git a/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java index ccac990a2..a8c79e735 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/EntityProcessor.java @@ -8,6 +8,9 @@ import edu.ie3.datamodel.exceptions.EntityProcessorException; import edu.ie3.datamodel.models.StandardUnits; import edu.ie3.datamodel.models.UniqueEntity; +import edu.ie3.datamodel.utils.Try; +import edu.ie3.datamodel.utils.Try.*; +import edu.ie3.util.exceptions.QuantityException; import java.lang.reflect.Method; import java.util.*; import javax.measure.Quantity; @@ -37,7 +40,7 @@ public abstract class EntityProcessor extends Processor< * * @param registeredClass the class the entity processor should be able to handle */ - protected EntityProcessor(Class registeredClass) { + protected EntityProcessor(Class registeredClass) throws EntityProcessorException { super(registeredClass); this.fieldNameToMethod = mapFieldNameToGetter(registeredClass, Collections.singleton(NODE_INTERNAL)); @@ -51,7 +54,7 @@ protected EntityProcessor(Class registeredClass) { * @return an optional Map with fieldName to fieldValue or an empty optional if an error occurred * during processing */ - public LinkedHashMap handleEntity(T entity) { + public LinkedHashMap handleEntity(T entity) throws EntityProcessorException { if (!registeredClass.equals(entity.getClass())) throw new EntityProcessorException( "Cannot process " @@ -62,33 +65,32 @@ public LinkedHashMap handleEntity(T entity) { + entity.getClass().getSimpleName() + ".class!"); - try { - return processObject(entity, fieldNameToMethod); - } catch (EntityProcessorException e) { - logger.error("Cannot process the entity{}.", entity, e); - throw new EntityProcessorException("Entity " + entity + " cannot be processed.", e); - } + return processObject(entity, fieldNameToMethod); } @Override - protected Optional handleProcessorSpecificQuantity( + protected Try handleProcessorSpecificQuantity( Quantity quantity, String fieldName) { return switch (fieldName) { case "energy", "eConsAnnual", "eStorage": - yield quantityValToOptionalString( - quantity.asType(Energy.class).to(StandardUnits.ENERGY_IN)); + yield Success.of( + quantityValToOptionalString(quantity.asType(Energy.class).to(StandardUnits.ENERGY_IN))); case "q": - yield quantityValToOptionalString( - quantity.asType(Power.class).to(StandardUnits.REACTIVE_POWER_IN)); + yield Success.of( + quantityValToOptionalString( + quantity.asType(Power.class).to(StandardUnits.REACTIVE_POWER_IN))); case "p", "pMax", "pOwn", "pThermal": - yield quantityValToOptionalString( - quantity.asType(Power.class).to(StandardUnits.ACTIVE_POWER_IN)); + yield Success.of( + quantityValToOptionalString( + quantity.asType(Power.class).to(StandardUnits.ACTIVE_POWER_IN))); default: - log.error( - "Cannot process quantity with value '{}' for field with name {} in input entity processing!", - quantity, - fieldName); - yield Optional.empty(); + yield Failure.of( + new QuantityException( + "Cannot process quantity with value '" + + quantity + + "' for field with name " + + fieldName + + " in input entity processing!")); }; } diff --git a/src/main/java/edu/ie3/datamodel/io/processor/Processor.java b/src/main/java/edu/ie3/datamodel/io/processor/Processor.java index 36f197db6..57e78433d 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/Processor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/Processor.java @@ -17,6 +17,9 @@ import edu.ie3.datamodel.models.input.system.characteristic.CharacteristicInput; import edu.ie3.datamodel.models.profile.LoadProfile; import edu.ie3.datamodel.models.voltagelevels.VoltageLevel; +import edu.ie3.datamodel.utils.Try; +import edu.ie3.datamodel.utils.Try.*; +import edu.ie3.util.exceptions.QuantityException; import java.beans.Introspector; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -72,7 +75,7 @@ public abstract class Processor { * * @param foreSeenClass Class and its children that are foreseen to be handled with this processor */ - protected Processor(Class foreSeenClass) { + protected Processor(Class foreSeenClass) throws EntityProcessorException { if (!getEligibleEntityClasses().contains(foreSeenClass)) throw new EntityProcessorException( "Cannot register class '" @@ -104,9 +107,10 @@ public int compare(String a, String b) { * Maps the foreseen table fields to the objects getters * * @param cls class to use for mapping - * @return an array of strings of all field values of the class + * @return a map of all field values of the class */ - protected SortedMap mapFieldNameToGetter(Class cls) { + protected SortedMap mapFieldNameToGetter(Class cls) + throws EntityProcessorException { return mapFieldNameToGetter(cls, Collections.emptyList()); } @@ -115,10 +119,10 @@ protected SortedMap mapFieldNameToGetter(Class cls) { * * @param cls class to use for mapping * @param ignoreFields A collection of all field names to ignore during process - * @return an array of strings of all field values of the class + * @return a map of all field values of the class */ protected SortedMap mapFieldNameToGetter( - Class cls, Collection ignoreFields) { + Class cls, Collection ignoreFields) throws EntityProcessorException { try { final LinkedHashMap resFieldNameToMethod = new LinkedHashMap<>(); Arrays.stream(Introspector.getBeanInfo(cls, Object.class).getPropertyDescriptors()) @@ -178,7 +182,7 @@ public static SortedMap putUuidFirst(Map unsorted) { * @return Mapping from field name to value as String representation */ protected LinkedHashMap processObject( - Object object, Map fieldNameToGetter) { + Object object, Map fieldNameToGetter) throws EntityProcessorException { try { LinkedHashMap resultMap = new LinkedHashMap<>(); for (Map.Entry entry : fieldNameToGetter.entrySet()) { @@ -207,7 +211,8 @@ protected LinkedHashMap processObject( * @param fieldName Name of the foreseen field * @return A String representation of the result */ - protected String processMethodResult(Object methodReturnObject, Method method, String fieldName) { + protected String processMethodResult(Object methodReturnObject, Method method, String fieldName) + throws EntityProcessorException { StringBuilder resultStringBuilder = new StringBuilder(); @@ -231,13 +236,17 @@ protected String processMethodResult(Object methodReturnObject, Method method, S ((Optional) methodReturnObject) .map( o -> { - if (o instanceof Quantity) { - return handleQuantity((Quantity) o, fieldName); - } else { - throw new EntityProcessorException( - "Handling of " - + o.getClass().getSimpleName() - + ".class instance wrapped into Optional is currently not supported by entity processors!"); + try { + if (o instanceof Quantity) { + return handleQuantity((Quantity) o, fieldName); + } else { + throw new EntityProcessorException( + "Handling of " + + o.getClass().getSimpleName() + + ".class instance wrapped into Optional is currently not supported by entity processors!"); + } + } catch (EntityProcessorException e) { + throw new RuntimeException(e); } }) .orElse("")); @@ -306,7 +315,8 @@ protected String processMethodResult(Object methodReturnObject, Method method, S * @return the resulting string of a VoltageLevel attribute value for the provided field or an * empty string when an invalid field name is provided */ - protected String processVoltageLevel(VoltageLevel voltageLevel, String fieldName) { + protected String processVoltageLevel(VoltageLevel voltageLevel, String fieldName) + throws EntityProcessorException { StringBuilder resultStringBuilder = new StringBuilder(); if (fieldName.equalsIgnoreCase(VOLT_LVL)) resultStringBuilder.append(voltageLevel.getId()); @@ -324,21 +334,26 @@ protected String processVoltageLevel(VoltageLevel voltageLevel, String fieldName * @return an optional string with the normalized to {@link StandardUnits} value of the quantity * or empty if an error occurred during processing */ - protected String handleQuantity(Quantity quantity, String fieldName) { - Optional optQuant; + protected String handleQuantity(Quantity quantity, String fieldName) + throws EntityProcessorException { + Try optQuant; if (specificQuantityFieldNames.contains(fieldName)) { optQuant = handleProcessorSpecificQuantity(quantity, fieldName); } else { - optQuant = quantityValToOptionalString(quantity); + optQuant = Success.of(quantityValToOptionalString(quantity)); } - return optQuant.orElseThrow( - () -> - new EntityProcessorException( - "Unable to process quantity value for attribute '" - + fieldName - + "' in entity " - + getRegisteredClass().getSimpleName() - + ".class.")); + + return optQuant + .transformF( + e -> + new EntityProcessorException( + "Unable to process quantity value for attribute '" + + fieldName + + "' in entity " + + getRegisteredClass().getSimpleName() + + ".class.", + e)) + .getOrThrow(); } /** @@ -354,7 +369,7 @@ protected String handleQuantity(Quantity quantity, String fieldName) { * @return an optional string with the normalized to {@link StandardUnits} value of the quantity * or empty if an error occurred during processing */ - protected abstract Optional handleProcessorSpecificQuantity( + protected abstract Try handleProcessorSpecificQuantity( Quantity quantity, String fieldName); protected String processUUIDArray(UUID[] uuids) { @@ -406,8 +421,8 @@ protected String processZonedDateTime(ZonedDateTime zonedDateTime) { * @param quantity Quantity to convert * @return A string of the quantity's value */ - protected Optional quantityValToOptionalString(Quantity quantity) { - return Optional.of(Double.toString(quantity.getValue().doubleValue())); + protected String quantityValToOptionalString(Quantity quantity) { + return Double.toString(quantity.getValue().doubleValue()); } /** diff --git a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java index 2230bd43e..2c3230d47 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java @@ -17,10 +17,9 @@ import edu.ie3.datamodel.models.timeseries.TimeSeries; import edu.ie3.datamodel.models.timeseries.TimeSeriesEntry; import edu.ie3.datamodel.models.value.Value; +import edu.ie3.datamodel.utils.Try; import java.util.*; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Wrapper providing the class specific processor to convert an instance of a {@link UniqueEntity} @@ -34,8 +33,6 @@ */ public class ProcessorProvider { - private static final Logger log = LoggerFactory.getLogger(ProcessorProvider.class); - /** unmodifiable map of all processors that has been provided on construction */ private final Map, EntityProcessor> entityProcessors; @@ -47,7 +44,7 @@ public class ProcessorProvider { timeSeriesProcessors; /** Get an instance of this class with all existing entity processors */ - public ProcessorProvider() { + public ProcessorProvider() throws EntityProcessorException { this.entityProcessors = init(allEntityProcessors()); this.timeSeriesProcessors = allTimeSeriesProcessors(); } @@ -70,15 +67,14 @@ public ProcessorProvider( this.timeSeriesProcessors = timeSeriesProcessors; } - public LinkedHashMap handleEntity(T entity) - throws ProcessorProviderException { - try { - EntityProcessor processor = getEntityProcessor(entity.getClass()); - return castProcessor(processor).handleEntity(entity); - } catch (ProcessorProviderException e) { - log.error("Exception occurred during entity handling.", e); - throw e; - } + public + Try, ProcessorProviderException> handleEntity(T entity) { + return Try.of(() -> getEntityProcessor(entity.getClass()), ProcessorProviderException.class) + .flatMap(ProcessorProvider::castProcessor) + .flatMap( + processor -> + Try.of(() -> processor.handleEntity(entity), EntityProcessorException.class) + .transformF(ProcessorProviderException::new)); } /** @@ -112,21 +108,21 @@ private EntityProcessor getEntityProcessor( * @param Type of the value inside the time series entries * @return A set of mappings from field name to value */ + @SuppressWarnings("unchecked") public , E extends TimeSeriesEntry, V extends Value> Set> handleTimeSeries(T timeSeries) throws ProcessorProviderException { TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); - try { - TimeSeriesProcessor processor = getTimeSeriesProcessor(key); - return processor.handleTimeSeries(timeSeries); - } catch (ProcessorProviderException e) { - log.error("Cannot handle the time series '{}'.", timeSeries, e); - throw new ProcessorProviderException( - "Cannot handle the time series {" + timeSeries + "}.", e); - } catch (EntityProcessorException e) { - log.error("Error during processing of time series.", e); - throw new EntityProcessorException("Error during processing of time series.", e); - } + return Try.of(() -> getTimeSeriesProcessor(key), ProcessorProviderException.class) + .flatMap( + processor -> + Try.of( + () -> + processor.handleTimeSeries( + (TimeSeries, Value>) timeSeries), + EntityProcessorException.class) + .transformF(ProcessorProviderException::new)) + .getOrThrow(); } /** @@ -238,7 +234,8 @@ private Map, EntityProcessor> allEntityProcessors() { + public static Collection> allEntityProcessors() + throws EntityProcessorException { Collection> resultingProcessors = new ArrayList<>(); resultingProcessors.addAll(allInputEntityProcessors()); resultingProcessors.addAll(allResultEntityProcessors()); @@ -250,7 +247,8 @@ public static Collection> allEntityProce * * @return a collection of all input processors */ - public static Collection> allInputEntityProcessors() { + public static Collection> allInputEntityProcessors() + throws EntityProcessorException { Collection> resultingProcessors = new ArrayList<>(); for (Class cls : InputEntityProcessor.eligibleEntityClasses) { resultingProcessors.add(new InputEntityProcessor(cls)); @@ -263,7 +261,8 @@ public static Collection> allInputEntity * * @return a collection of all result processors */ - public static Collection> allResultEntityProcessors() { + public static Collection> allResultEntityProcessors() + throws EntityProcessorException { Collection> resultingProcessors = new ArrayList<>(); for (Class cls : ResultEntityProcessor.eligibleEntityClasses) { resultingProcessors.add(new ResultEntityProcessor(cls)); @@ -276,6 +275,7 @@ public static Collection> allResultEntit * * @return A mapping from eligible combinations to processors */ + @SuppressWarnings("unchecked") public static Map< TimeSeriesProcessorKey, TimeSeriesProcessor< @@ -285,23 +285,28 @@ public static Collection> allResultEntit .collect( Collectors.toMap( key -> key, - key -> - new TimeSeriesProcessor<>( + key -> { + try { + return new TimeSeriesProcessor<>( (Class, Value>>) key.getTimeSeriesClass(), (Class>) key.getEntryClass(), - (Class) key.getValueClass()))); + (Class) key.getValueClass()); + } catch (EntityProcessorException e) { + throw new RuntimeException(e); + } + })); } @SuppressWarnings("unchecked cast") - private static EntityProcessor castProcessor( - EntityProcessor processor) throws ProcessorProviderException { - try { - return (EntityProcessor) processor; - } catch (ClassCastException ex) { - throw new ProcessorProviderException( - "Cannot cast processor with registered class '" - + processor.getRegisteredClass().getSimpleName() - + "'. This indicates a fatal problem with the processor mapping!"); - } + private static + Try, ProcessorProviderException> castProcessor( + EntityProcessor processor) { + return Try.of(() -> (EntityProcessor) processor, ClassCastException.class) + .transformF( + e -> + new ProcessorProviderException( + "Cannot cast processor with registered class '" + + processor.getRegisteredClass().getSimpleName() + + "'. This indicates a fatal problem with the processor mapping!")); } } diff --git a/src/main/java/edu/ie3/datamodel/io/processor/input/InputEntityProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/input/InputEntityProcessor.java index 16a75b725..398f404aa 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/input/InputEntityProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/input/InputEntityProcessor.java @@ -5,6 +5,7 @@ */ package edu.ie3.datamodel.io.processor.input; +import edu.ie3.datamodel.exceptions.EntityProcessorException; import edu.ie3.datamodel.io.processor.EntityProcessor; import edu.ie3.datamodel.io.source.TimeSeriesMappingSource; import edu.ie3.datamodel.models.input.*; @@ -73,7 +74,8 @@ public class InputEntityProcessor extends EntityProcessor { StorageTypeInput.class, WecTypeInput.class); - public InputEntityProcessor(Class registeredClass) { + public InputEntityProcessor(Class registeredClass) + throws EntityProcessorException { super(registeredClass); } diff --git a/src/main/java/edu/ie3/datamodel/io/processor/result/ResultEntityProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/result/ResultEntityProcessor.java index 93a792eda..177ed1909 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/result/ResultEntityProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/result/ResultEntityProcessor.java @@ -5,6 +5,7 @@ */ package edu.ie3.datamodel.io.processor.result; +import edu.ie3.datamodel.exceptions.EntityProcessorException; import edu.ie3.datamodel.io.factory.result.SystemParticipantResultFactory; import edu.ie3.datamodel.io.processor.EntityProcessor; import edu.ie3.datamodel.models.StandardUnits; @@ -17,6 +18,9 @@ import edu.ie3.datamodel.models.result.system.*; import edu.ie3.datamodel.models.result.thermal.CylindricalStorageResult; import edu.ie3.datamodel.models.result.thermal.ThermalHouseResult; +import edu.ie3.datamodel.utils.Try; +import edu.ie3.datamodel.utils.Try.*; +import edu.ie3.util.exceptions.QuantityException; import java.util.*; import javax.measure.Quantity; import javax.measure.quantity.Energy; @@ -56,29 +60,35 @@ public class ResultEntityProcessor extends EntityProcessor { EmResult.class, FlexOptionsResult.class); - public ResultEntityProcessor(Class registeredClass) { + public ResultEntityProcessor(Class registeredClass) + throws EntityProcessorException { super(registeredClass); } @Override - protected Optional handleProcessorSpecificQuantity( + protected Try handleProcessorSpecificQuantity( Quantity quantity, String fieldName) { return switch (fieldName) { case "energy", "eConsAnnual", "eStorage": - yield quantityValToOptionalString( - quantity.asType(Energy.class).to(StandardUnits.ENERGY_RESULT)); + yield Success.of( + quantityValToOptionalString( + quantity.asType(Energy.class).to(StandardUnits.ENERGY_RESULT))); case "q": - yield quantityValToOptionalString( - quantity.asType(Power.class).to(StandardUnits.REACTIVE_POWER_RESULT)); + yield Success.of( + quantityValToOptionalString( + quantity.asType(Power.class).to(StandardUnits.REACTIVE_POWER_RESULT))); case "p", "pMax", "pOwn", "pThermal", "pRef", "pMin": - yield quantityValToOptionalString( - quantity.asType(Power.class).to(StandardUnits.ACTIVE_POWER_RESULT)); + yield Success.of( + quantityValToOptionalString( + quantity.asType(Power.class).to(StandardUnits.ACTIVE_POWER_RESULT))); default: - log.error( - "Cannot process quantity with value '{}' for field with name {} in result entity processing!", - quantity, - fieldName); - yield Optional.empty(); + yield Failure.of( + new QuantityException( + "Cannot process quantity with value '" + + quantity + + "' for field with name " + + fieldName + + " in result entity processing!")); }; } diff --git a/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java index 7daabe432..ee72b5942 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java @@ -66,7 +66,8 @@ public class TimeSeriesProcessor< private final String[] flattenedHeaderElements; - public TimeSeriesProcessor(Class timeSeriesClass, Class entryClass, Class valueClass) { + public TimeSeriesProcessor(Class timeSeriesClass, Class entryClass, Class valueClass) + throws EntityProcessorException { super(timeSeriesClass); /* Check, if this processor can handle the foreseen combination of time series, entry and value */ @@ -101,7 +102,8 @@ public TimeSeriesProcessor(Class timeSeriesClass, Class entryClass, Class< * @return A mapping from field name to a tuple of source information and equivalent getter method */ private SortedMap buildFieldToSource( - Class timeSeriesClass, Class entryClass, Class valueClass) { + Class timeSeriesClass, Class entryClass, Class valueClass) + throws EntityProcessorException { /* Get the mapping from field name to getter method ignoring the getter for returning all entries */ Map timeSeriesMapping = mapFieldNameToGetter(timeSeriesClass, Arrays.asList("entries", "uuid", "type")) @@ -187,7 +189,8 @@ public LinkedHashMap handleEntity(TimeSeries entity) { * @param timeSeries Time series to handle * @return A set of mappings from field name to value */ - public Set> handleTimeSeries(T timeSeries) { + public Set> handleTimeSeries(T timeSeries) + throws EntityProcessorException { TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); if (!registeredKey.equals(key)) throw new EntityProcessorException( @@ -219,7 +222,7 @@ public Set> handleTimeSeries(T timeSeries) { * @param entry Actual entry to handle * @return A sorted map from field name to value as String representation */ - private Map handleEntry(T timeSeries, E entry) { + private Map handleEntry(T timeSeries, E entry) throws EntityProcessorException { /* Handle the information in the time series */ Map timeSeriesFieldToMethod = extractFieldToMethod(TIMESERIES); LinkedHashMap timeSeriesResults = diff --git a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java index 729905586..7424cf060 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java @@ -5,10 +5,7 @@ */ package edu.ie3.datamodel.io.sink; -import edu.ie3.datamodel.exceptions.ConnectorException; -import edu.ie3.datamodel.exceptions.ExtractorException; -import edu.ie3.datamodel.exceptions.ProcessorProviderException; -import edu.ie3.datamodel.exceptions.SinkException; +import edu.ie3.datamodel.exceptions.*; import edu.ie3.datamodel.io.connectors.CsvFileConnector; import edu.ie3.datamodel.io.csv.BufferedCsvWriter; import edu.ie3.datamodel.io.extractor.Extractor; @@ -59,7 +56,7 @@ public class CsvFileSink implements InputDataSink, OutputDataSink { private final String csvSep; - public CsvFileSink(Path baseFolderPath) { + public CsvFileSink(Path baseFolderPath) throws EntityProcessorException { this(baseFolderPath, new FileNamingStrategy(), ","); } @@ -72,7 +69,8 @@ public CsvFileSink(Path baseFolderPath) { * @param fileNamingStrategy the data sink file naming strategy that should be used * @param csvSep the csv file separator that should be use */ - public CsvFileSink(Path baseFolderPath, FileNamingStrategy fileNamingStrategy, String csvSep) { + public CsvFileSink(Path baseFolderPath, FileNamingStrategy fileNamingStrategy, String csvSep) + throws EntityProcessorException { this(baseFolderPath, new ProcessorProvider(), fileNamingStrategy, csvSep); } @@ -292,9 +290,9 @@ private , V extends Value> void persistTimeSeries( * @param bounded to be all unique entities */ private void write(C entity) { - LinkedHashMap entityFieldData; try { - entityFieldData = csvEntityFieldData(processorProvider.handleEntity(entity)); + LinkedHashMap entityFieldData = + processorProvider.handleEntity(entity).map(this::csvEntityFieldData).getOrThrow(); String[] headerElements = processorProvider.getHeaderElements(entity.getClass()); BufferedCsvWriter writer = connector.getOrInitWriter(entity.getClass(), headerElements, csvSep); diff --git a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java index b57d04d8b..7c2f7b9f8 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/InfluxDbSink.java @@ -5,6 +5,7 @@ */ package edu.ie3.datamodel.io.sink; +import edu.ie3.datamodel.exceptions.EntityProcessorException; import edu.ie3.datamodel.exceptions.ProcessorProviderException; import edu.ie3.datamodel.io.connectors.InfluxDbConnector; import edu.ie3.datamodel.io.naming.EntityPersistenceNamingStrategy; @@ -30,8 +31,6 @@ public class InfluxDbSink implements OutputDataSink { /** Field name for input model uuid field in result entities */ private static final String FIELD_NAME_INPUT = "inputModel"; - private static final String ERROR_MESSAGE = "Cannot persist provided entity '{}'. Exception: {}"; - private final InfluxDbConnector connector; private final EntityPersistenceNamingStrategy entityPersistenceNamingStrategy; private final ProcessorProvider processorProvider; @@ -43,8 +42,8 @@ public class InfluxDbSink implements OutputDataSink { * @param entityPersistenceNamingStrategy needed to create measurement names for entities */ public InfluxDbSink( - InfluxDbConnector connector, - EntityPersistenceNamingStrategy entityPersistenceNamingStrategy) { + InfluxDbConnector connector, EntityPersistenceNamingStrategy entityPersistenceNamingStrategy) + throws EntityProcessorException { this.connector = connector; this.entityPersistenceNamingStrategy = entityPersistenceNamingStrategy; this.processorProvider = @@ -58,7 +57,7 @@ public InfluxDbSink( * * @param connector needed for database connection */ - public InfluxDbSink(InfluxDbConnector connector) { + public InfluxDbSink(InfluxDbConnector connector) throws EntityProcessorException { this(connector, new EntityPersistenceNamingStrategy()); } @@ -127,21 +126,16 @@ private Point transformToPoint(ResultEntity entity) throws ProcessorProviderExce */ private Point transformToPoint(ResultEntity entity, String measurementName) throws ProcessorProviderException { - LinkedHashMap entityFieldData; - try { - entityFieldData = processorProvider.handleEntity(entity); - entityFieldData.remove(FIELD_NAME_TIME); - return Point.measurement(transformToMeasurementName(measurementName)) - .time(entity.getTime().toInstant().toEpochMilli(), TimeUnit.MILLISECONDS) - .tag("input_model", entityFieldData.remove(FIELD_NAME_INPUT)) - .tag("scenario", connector.getScenarioName()) - .fields(Collections.unmodifiableMap(entityFieldData)) - .build(); - } catch (ProcessorProviderException e) { - log.error(ERROR_MESSAGE, entity.getClass().getSimpleName(), e); - throw new ProcessorProviderException(e); - } + LinkedHashMap entityFieldData = + processorProvider.handleEntity(entity).getOrThrow(); + entityFieldData.remove(FIELD_NAME_TIME); + return Point.measurement(transformToMeasurementName(measurementName)) + .time(entity.getTime().toInstant().toEpochMilli(), TimeUnit.MILLISECONDS) + .tag("input_model", entityFieldData.remove(FIELD_NAME_INPUT)) + .tag("scenario", connector.getScenarioName()) + .fields(Collections.unmodifiableMap(entityFieldData)) + .build(); } /** @@ -156,22 +150,16 @@ private , V extends Value> Set transformToPo TimeSeries timeSeries) throws ProcessorProviderException { if (timeSeries.getEntries().isEmpty()) return Collections.emptySet(); - try { - Optional measurementName = entityPersistenceNamingStrategy.getEntityName(timeSeries); - if (measurementName.isEmpty()) { - String valueClassName = - timeSeries.getEntries().iterator().next().getValue().getClass().getSimpleName(); - log.warn( - "I could not get a measurement name for TimeSeries value class {}. I am using it's value's simple name instead.", - valueClassName); - return transformToPoints(timeSeries, valueClassName); - } - return transformToPoints(timeSeries, measurementName.get()); - } catch (ProcessorProviderException e) { - log.error(ERROR_MESSAGE, timeSeries.getClass().getSimpleName(), e); - throw new ProcessorProviderException( - "Cannot persist provided time series {" + timeSeries.getClass().getSimpleName() + "}", e); + Optional measurementName = entityPersistenceNamingStrategy.getEntityName(timeSeries); + if (measurementName.isEmpty()) { + String valueClassName = + timeSeries.getEntries().iterator().next().getValue().getClass().getSimpleName(); + log.warn( + "I could not get a measurement name for TimeSeries value class {}. I am using it's value's simple name instead.", + valueClassName); + return transformToPoints(timeSeries, valueClassName); } + return transformToPoints(timeSeries, measurementName.get()); } /** @@ -184,25 +172,19 @@ private , V extends Value> Set transformToPo private , V extends Value> Set transformToPoints( TimeSeries timeSeries, String measurementName) throws ProcessorProviderException { Set points = new HashSet<>(); - try { - Set> entityFieldData = - processorProvider.handleTimeSeries(timeSeries); - - for (LinkedHashMap dataMapping : entityFieldData) { - String timeString = dataMapping.remove(FIELD_NAME_TIME); - long timeMillis = ZonedDateTime.parse(timeString).toInstant().toEpochMilli(); - Point point = - Point.measurement(transformToMeasurementName(measurementName)) - .time(timeMillis, TimeUnit.MILLISECONDS) - .tag("scenario", connector.getScenarioName()) - .fields(Collections.unmodifiableMap(dataMapping)) - .build(); - points.add(point); - } - } catch (ProcessorProviderException e) { - log.error(ERROR_MESSAGE, timeSeries.getClass().getSimpleName(), e); - throw new ProcessorProviderException( - "Cannot persist provided time series {" + timeSeries.getClass().getSimpleName() + "}", e); + Set> entityFieldData = + processorProvider.handleTimeSeries(timeSeries); + + for (LinkedHashMap dataMapping : entityFieldData) { + String timeString = dataMapping.remove(FIELD_NAME_TIME); + long timeMillis = ZonedDateTime.parse(timeString).toInstant().toEpochMilli(); + Point point = + Point.measurement(transformToMeasurementName(measurementName)) + .time(timeMillis, TimeUnit.MILLISECONDS) + .tag("scenario", connector.getScenarioName()) + .fields(Collections.unmodifiableMap(dataMapping)) + .build(); + points.add(point); } return points; } @@ -222,11 +204,7 @@ private Set extractPoints(C entity) Set points = new HashSet<>(); /* Distinguish between result models and time series */ if (entity instanceof ResultEntity resultEntity) { - try { - points.add(transformToPoint(resultEntity)); - } catch (ProcessorProviderException e) { - log.error(ERROR_MESSAGE, entity.getClass().getSimpleName(), e); - } + points.add(transformToPoint(resultEntity)); } else if (entity instanceof TimeSeries timeSeries) { points.addAll(transformToPoints(timeSeries)); } else { diff --git a/src/main/java/edu/ie3/datamodel/utils/Try.java b/src/main/java/edu/ie3/datamodel/utils/Try.java index 0d1c571d5..06a9a5526 100644 --- a/src/main/java/edu/ie3/datamodel/utils/Try.java +++ b/src/main/java/edu/ie3/datamodel/utils/Try.java @@ -303,6 +303,18 @@ public T get() { return data; } + /** + * Method to create a {@link Success} by applying data. + * + * @param data that should be wrapped by the {@link Success} + * @return a new {@link Success} + * @param type of data + * @param type of exception + */ + public static Success of(D data) { + return new Success<>(data); + } + /** * Returns an empty {@link Success}. * diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy index 7948503ce..5fffb0fa1 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/ProcessorProviderTest.groovy @@ -45,6 +45,7 @@ import edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileInput import edu.ie3.datamodel.models.value.* +import edu.ie3.datamodel.utils.Try import edu.ie3.test.common.TimeSeriesTestData import edu.ie3.util.TimeUtil import spock.lang.Specification @@ -224,20 +225,27 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData PvResult pvResult = new PvResult(uuid, TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"), inputModel, p, q) and: - LinkedHashMap resultMap = provider.handleEntity(pvResult) + Try, ProcessorProviderException> result = provider.handleEntity(pvResult) then: + result.success + Map resultMap = result.data.get() + resultMap.size() == 5 resultMap == expectedMap when: - provider.handleEntity(new WecResult(uuid, TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"), inputModel, p, q)) + Try, ProcessorProviderException> entityTry = provider.handleEntity(new WecResult(uuid, TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"), inputModel, p, q)) then: - Exception ex = thrown() - ex.class == ProcessorProviderException - List.of("Cannot find a suitable processor for provided class with name 'WecResult'. This provider's processors can process: ","PvResult", "EvResult") - .forEach {str -> ex.message.contains(str)} + entityTry.failure + ProcessorProviderException ex = entityTry.exception.get() + [ + "Cannot find a suitable processor for provided class with name 'WecResult'. This provider's processors can process: ", + "PvResult", + "EvResult" + ] + .every { str -> ex.message.contains(str) } } def "A ProcessorProvider returns an empty Optional, if none of the assigned processors is able to handle a time series"() { @@ -254,8 +262,7 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData then: Exception ex = thrown() ex.class == ProcessorProviderException - ex.message == "Cannot handle the time series {" + individualIntTimeSeries + "}." - ex.cause.message == "Cannot find processor for time series combination 'TimeSeriesProcessorKey{timeSeriesClass=class edu.ie3.datamodel.models.timeseries.individual.IndividualTimeSeries, entryClass=class edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue, valueClass=class edu.ie3.datamodel.models.timeseries.IntValue}'. Either your provider is not properly initialized or there is no implementation to process this entity class!)" + ex.message == "Cannot find processor for time series combination 'TimeSeriesProcessorKey{timeSeriesClass=class edu.ie3.datamodel.models.timeseries.individual.IndividualTimeSeries, entryClass=class edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue, valueClass=class edu.ie3.datamodel.models.timeseries.IntValue}'. Either your provider is not properly initialized or there is no implementation to process this entity class!)" } def "A ProcessorProvider handles a time series correctly"() { @@ -267,7 +274,7 @@ class ProcessorProviderTest extends Specification implements TimeSeriesTestData ProcessorProvider provider = new ProcessorProvider([], timeSeriesProcessorMap) when: - Set> actual = provider.handleTimeSeries(individualEnergyPriceTimeSeries) + Set> actual = provider.handleTimeSeries(individualEnergyPriceTimeSeries) then: actual == individualEnergyPriceTimeSeriesProcessed diff --git a/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy index aef13bce0..65dc1e1a6 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/processor/input/InputEntityProcessorTest.groovy @@ -291,7 +291,7 @@ class InputEntityProcessorTest extends Specification { ] when: - LinkedHashMap actual = processor.handleEntity(validNode) + Map actual = processor.handleEntity(validNode) then: actual == expected @@ -310,7 +310,7 @@ class InputEntityProcessorTest extends Specification { ] when: - LinkedHashMap actual = processor.handleEntity(validNode) + Map actual = processor.handleEntity(validNode) then: actual == expected @@ -328,7 +328,7 @@ class InputEntityProcessorTest extends Specification { ] when: - LinkedHashMap actual = processor.handleEntity(validNode) + Map actual = processor.handleEntity(validNode) then: actual == expected @@ -344,7 +344,7 @@ class InputEntityProcessorTest extends Specification { ] when: - LinkedHashMap actual = processor.handleEntity(operator) + Map actual = processor.handleEntity(operator) then: actual == expected @@ -381,7 +381,7 @@ class InputEntityProcessorTest extends Specification { ] when: - LinkedHashMap actual = processor.handleEntity(parameters) + Map actual = processor.handleEntity(parameters) then: actual == expected From 2aebb8c86ba3f9fcc9f43c916b7efc09c2de0917 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 1 Aug 2023 10:31:45 +0200 Subject: [PATCH 08/11] Applied type parameters to method call --- .../ie3/datamodel/io/processor/ProcessorProvider.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java index 2c3230d47..60ac43f1f 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java @@ -108,19 +108,14 @@ private EntityProcessor getEntityProcessor( * @param Type of the value inside the time series entries * @return A set of mappings from field name to value */ - @SuppressWarnings("unchecked") public , E extends TimeSeriesEntry, V extends Value> Set> handleTimeSeries(T timeSeries) throws ProcessorProviderException { TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries); - return Try.of(() -> getTimeSeriesProcessor(key), ProcessorProviderException.class) + return Try.of(() -> this.getTimeSeriesProcessor(key), ProcessorProviderException.class) .flatMap( processor -> - Try.of( - () -> - processor.handleTimeSeries( - (TimeSeries, Value>) timeSeries), - EntityProcessorException.class) + Try.of(() -> processor.handleTimeSeries(timeSeries), EntityProcessorException.class) .transformF(ProcessorProviderException::new)) .getOrThrow(); } From f83abfb3740abd8e274df4b038cdb2a47ae6611c Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 1 Aug 2023 10:50:52 +0200 Subject: [PATCH 09/11] Avoided usage of RuntimeException --- .../ie3/datamodel/io/processor/Processor.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/processor/Processor.java b/src/main/java/edu/ie3/datamodel/io/processor/Processor.java index 57e78433d..830fbef02 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/Processor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/Processor.java @@ -236,20 +236,20 @@ protected String processMethodResult(Object methodReturnObject, Method method, S ((Optional) methodReturnObject) .map( o -> { - try { - if (o instanceof Quantity) { - return handleQuantity((Quantity) o, fieldName); - } else { - throw new EntityProcessorException( - "Handling of " - + o.getClass().getSimpleName() - + ".class instance wrapped into Optional is currently not supported by entity processors!"); - } - } catch (EntityProcessorException e) { - throw new RuntimeException(e); + if (o instanceof Quantity) { + return Try.of( + () -> handleQuantity((Quantity) o, fieldName), + EntityProcessorException.class); + } else { + return Failure.of( + new EntityProcessorException( + "Handling of " + + o.getClass().getSimpleName() + + ".class instance wrapped into Optional is currently not supported by entity processors!")); } }) - .orElse("")); + .orElse(Success.of("")) // (in case of empty optional) + .getOrThrow()); case "ZonedDateTime" -> resultStringBuilder.append( processZonedDateTime((ZonedDateTime) methodReturnObject)); case "OperationTime" -> resultStringBuilder.append( From 3fcdca33e77651a48f283dad328ec27a3382cb05 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 1 Aug 2023 12:30:42 +0200 Subject: [PATCH 10/11] Avoided usage of RuntimeException in time series processor map creation --- .../io/processor/ProcessorProvider.java | 37 +++++++++++-------- .../timeseries/TimeSeriesProcessor.java | 4 ++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java index 60ac43f1f..8f3bb42f4 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/ProcessorProvider.java @@ -6,6 +6,7 @@ package edu.ie3.datamodel.io.processor; import edu.ie3.datamodel.exceptions.EntityProcessorException; +import edu.ie3.datamodel.exceptions.FailureException; import edu.ie3.datamodel.exceptions.ProcessorProviderException; import edu.ie3.datamodel.io.processor.input.InputEntityProcessor; import edu.ie3.datamodel.io.processor.result.ResultEntityProcessor; @@ -19,6 +20,7 @@ import edu.ie3.datamodel.models.value.Value; import edu.ie3.datamodel.utils.Try; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -275,21 +277,26 @@ public static Collection> allResultEntit TimeSeriesProcessorKey, TimeSeriesProcessor< TimeSeries, Value>, TimeSeriesEntry, Value>> - allTimeSeriesProcessors() { - return TimeSeriesProcessor.eligibleKeys.stream() - .collect( - Collectors.toMap( - key -> key, - key -> { - try { - return new TimeSeriesProcessor<>( - (Class, Value>>) key.getTimeSeriesClass(), - (Class>) key.getEntryClass(), - (Class) key.getValueClass()); - } catch (EntityProcessorException e) { - throw new RuntimeException(e); - } - })); + allTimeSeriesProcessors() throws EntityProcessorException { + try { + return Try.scanStream( + TimeSeriesProcessor.eligibleKeys.stream() + .map( + key -> + Try.of( + () -> + new TimeSeriesProcessor<>( + (Class, Value>>) + key.getTimeSeriesClass(), + (Class>) key.getEntryClass(), + (Class) key.getValueClass()), + EntityProcessorException.class)), + "list of processors") + .getOrThrow() + .collect(Collectors.toMap(TimeSeriesProcessor::getRegisteredKey, Function.identity())); + } catch (FailureException e) { + throw new EntityProcessorException(e.getCause()); + } } @SuppressWarnings("unchecked cast") diff --git a/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java b/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java index ee72b5942..52f1a602a 100644 --- a/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java +++ b/src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java @@ -92,6 +92,10 @@ public TimeSeriesProcessor(Class timeSeriesClass, Class entryClass, Class< this.flattenedHeaderElements = fieldToSource.keySet().toArray(new String[0]); } + public TimeSeriesProcessorKey getRegisteredKey() { + return registeredKey; + } + /** * Collects the mapping, where to find which information and how to get them (in terms of getter * method). From 087129430cd948c1f9355b29488d064cab10f074 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 1 Aug 2023 12:41:36 +0200 Subject: [PATCH 11/11] Fixed code smell --- src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java index 7424cf060..da67f99a0 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java @@ -277,7 +277,6 @@ private , V extends Value> void persistTimeSeries( } }); } catch (ProcessorProviderException e) { - log.error("Exception occurred during processor request: ", e); throw new ProcessorProviderException("Exception occurred during processor request: ", e); } }