ref #7980, ref #8871 improve error handling and verification in aggregations
authorAndreas Müller <a.mueller@bgbm.org>
Thu, 7 Oct 2021 17:33:49 +0000 (19:33 +0200)
committerAndreas Müller <a.mueller@bgbm.org>
Thu, 7 Oct 2021 17:42:35 +0000 (19:42 +0200)
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/AggregationException.java [new file with mode: 0644]
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/DescriptionAggregationBase.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/DistributionAggregation.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/StructuredDescriptionAggregation.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/description/StructuredDescriptionAggregationTest.java

diff --git a/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/AggregationException.java b/cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/AggregationException.java
new file mode 100644 (file)
index 0000000..74386f8
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+* Copyright (C) 2021 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.api.service.description;
+
+/**
+ * @author a.mueller
+ * @since 07.10.2021
+ */
+public class AggregationException extends RuntimeException {
+
+    private static final long serialVersionUID = -883213782380432255L;
+
+    public AggregationException() {
+        super();
+    }
+
+    public AggregationException(String message, Throwable cause, boolean enableSuppression,
+            boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+    public AggregationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public AggregationException(String message) {
+        super(message);
+    }
+
+    public AggregationException(Throwable cause) {
+        super(cause);
+    }
+
+}
index c8a9e049d93307fc97323342047131517acede15..ff87df2648d90a9a54412cc29e177fc373e4f355 100644 (file)
@@ -122,10 +122,13 @@ public abstract class DescriptionAggregationBase<T extends DescriptionAggregatio
             try {
                 preAggregate(subMonitor);
             } catch (Exception e) {
-                result.addException(new RuntimeException("Unhandled error during pre-aggregation", e));
-                result.setError();
-                done();
-                return result;
+                return handleException(e, "Unhandled error during pre-aggregation");
+            }
+
+            try {
+                verifyConfiguration(subMonitor);
+            } catch (Exception e) {
+                return handleException(e, "Unhandled error during configuration check");
             }
 
             subMonitor.worked(preAccumulateTicks);
@@ -138,11 +141,7 @@ public abstract class DescriptionAggregationBase<T extends DescriptionAggregatio
             try {
                 aggregate(taxonNodeIdList, aggregateMonitor);
             } catch (Exception e) {
-                result.addException(new RuntimeException("Unhandled error during aggregation: " + e.getMessage() , e));
-                e.printStackTrace();
-                result.setError();
-                done();
-                return result;
+                return handleException(e, "Unhandled error during aggregation");
             }
 
             double end = System.currentTimeMillis();
@@ -155,7 +154,20 @@ public abstract class DescriptionAggregationBase<T extends DescriptionAggregatio
             result.addException(new RuntimeException("Unhandled error during doInvoke", e));
             return result;
         }
+    }
 
+    private UpdateResult handleException(Exception e, String unhandledMessage) {
+        Exception ex;
+        if (e instanceof AggregationException){
+            ex = e;
+        }else{
+            ex = new RuntimeException(unhandledMessage + ": " + e.getMessage() , e);
+            e.printStackTrace();
+        }
+        result.addException(ex);
+        result.setError();
+        done();
+        return result;
     }
 
     protected void aggregate(List<Integer> taxonNodeIdList, IProgressMonitor subMonitor)  throws JvmLimitsException {
@@ -330,6 +342,8 @@ public abstract class DescriptionAggregationBase<T extends DescriptionAggregatio
 
     protected abstract void preAggregate(IProgressMonitor monitor);
 
+    protected abstract void verifyConfiguration(IProgressMonitor monitor);
+
     /**
      * hook for initializing object when a new transaction starts
      */
index b651f5a9e053686a0267a5b59579d37f457f9d6e..6531ee47400b75741b55c08f25306cecefa83379 100644 (file)
@@ -96,6 +96,7 @@ public class DistributionAggregation
 // ******************* CONSTRUCTOR *********************************/
 
     public DistributionAggregation() {}
+
     @Override
     protected String pluralDataType(){
         return "distributions";
@@ -120,6 +121,18 @@ public class DistributionAggregation
         logger.info("Time elapsed for making super areas : " + (end2 - end1) / (1000) + "s");
     }
 
+    @Override
+    protected void verifyConfiguration(IProgressMonitor monitor){
+        if (!AggregationSourceMode.list(AggregationMode.ToParent, AggregationType.Distribution)
+            .contains(getConfig().getToParentSourceMode())){
+            throw new AggregationException("Unsupported source mode for to-parent aggregation: " + getConfig().getToParentSourceMode());
+        }
+        if (!AggregationSourceMode.list(AggregationMode.WithinTaxon, AggregationType.Distribution)
+                .contains(getConfig().getWithinTaxonSourceMode())){
+                throw new AggregationException("Unsupported source mode for within-taxon aggregation: " + getConfig().getToParentSourceMode());
+        }
+    }
+
     @Override
     protected void initTransaction() {
     }
index c34b3299b9b0ceccc6a96449c2e8668b40e7460c..e1bfe42633a15f5ad2bacd0124e6526417b406c9 100644 (file)
@@ -40,6 +40,7 @@ import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
 import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.reference.ICdmTarget;
 import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
@@ -79,6 +80,17 @@ public class StructuredDescriptionAggregation
         logger.info("Time elapsed for pre-accumulate() : " + (end1 - start) / (1000) + "s");
     }
 
+    @Override
+    protected void verifyConfiguration(IProgressMonitor monitor){
+        if (!AggregationSourceMode.list(AggregationMode.ToParent, AggregationType.StructuredDescription)
+            .contains(getConfig().getToParentSourceMode())){
+            throw new AggregationException("Unsupported source mode for to-parent aggregation: " + getConfig().getToParentSourceMode());
+        }
+        if (!AggregationSourceMode.list(AggregationMode.WithinTaxon, AggregationType.StructuredDescription)
+                .contains(getConfig().getWithinTaxonSourceMode())){
+                throw new AggregationException("Unsupported source mode for within-taxon aggregation: " + getConfig().getWithinTaxonSourceMode());
+        }
+    }
 
     private boolean hasCharacterData(DescriptionElementBase element) {
         return hasCategoricalData(element) || hasQuantitativeData(element);
@@ -164,11 +176,18 @@ public class StructuredDescriptionAggregation
         //remove remaining sources-to-be-removed
         for (IdentifiableSource sourceToRemove : sourcesToRemove) {
             targetDescription.removeSource(sourceToRemove);
-            if (sourceToRemove.getCdmSource() != null){
-                @SuppressWarnings("unchecked")
-                T descriptionToDelete = ((T)sourceToRemove.getCdmSource());
-                ((IDescribable<T>)descriptionToDelete.describedEntity()).removeDescription(descriptionToDelete);
-                structuredResultHolder.descriptionsToDelete.add(descriptionToDelete);
+            ICdmTarget target = sourceToRemove.getCdmSource();
+            if (target != null){
+                if (target.isInstanceOf(DescriptionBase.class)){
+                    @SuppressWarnings("unchecked")
+                    T descriptionToDelete = ((T)sourceToRemove.getCdmSource());
+                    ((IDescribable<T>)descriptionToDelete.describedEntity()).removeDescription(descriptionToDelete);
+                    structuredResultHolder.descriptionsToDelete.add(descriptionToDelete);
+                }else if (target.isInstanceOf(Taxon.class)){
+                    //nothing to do for now
+                } else {
+                    throw new AggregationException("CdmLink target type not yet supported: " + target.getClass().getSimpleName());
+                }
             }
         }
     }
@@ -206,10 +225,10 @@ public class StructuredDescriptionAggregation
             }else if (newTarget instanceof Taxon){
                 //nothing to do for now (we do not support reuse of sources linking to different taxa yet)
             }else{
-                throw new IllegalStateException("Sources not linking to a description or a taxon instance currently not yet supported.");
+                throw new AggregationException("Sources not linking to a description or a taxon instance currently not yet supported.");
             }
         }else{
-            throw new IllegalStateException("Sources not linking to another CdmBase instance currently not yet supported.");
+            throw new AggregationException("Sources not linking to another CdmBase instance currently not yet supported.");
         }
     }
 
@@ -281,7 +300,7 @@ public class StructuredDescriptionAggregation
                     }else if (newTarget instanceof Taxon){
                         return newTarget.equals(existingTarget);
                     }else{
-                        throw new IllegalStateException("Other classes then SpecimenDescription and TaxonDescription are not yet supported. But was: " + newTarget.getClass());
+                        throw new AggregationException("Other classes then SpecimenDescription and TaxonDescription are not yet supported. But was: " + newTarget.getClass());
                     }
                 }
             }
@@ -344,7 +363,7 @@ public class StructuredDescriptionAggregation
         }else if (targetElement.isInstanceOf(QuantitativeData.class)){
             mergeDescriptionElement((QuantitativeData)targetElement, (QuantitativeData)newElement);
         }else{
-            throw new IllegalArgumentException("Class not supported: " + targetElement.getClass().getName());
+            throw new AggregationException("Class not supported: " + targetElement.getClass().getName());
         }
     }
 
@@ -484,16 +503,18 @@ public class StructuredDescriptionAggregation
                             Taxon taxon = ((TaxonDescription) desc).getTaxon();
                             source.setCdmSource(taxon);
                         }else {
-                            throw new IllegalStateException("Description type not yet supported for aggregation source mode TAXON: " + desc.getClass().getSimpleName() );
+                            throw new AggregationException("Description type not yet supported for aggregation source mode TAXON: " + desc.getClass().getSimpleName() );
                         }
                         break;
-                    case ALL: //not yet supported
-                    case ALL_SAMEVALUE: //makes no sense
                     case NONE:
                         source = null;
                         break;
+                    case ALL: //not yet supported
+                        throw new AggregationException("Source mode not yet supported: " + sourceMode);
+                    case ALL_SAMEVALUE: //makes no sense
+                        throw new AggregationException("Illegal source mode: " + sourceMode);
                     default:
-                        throw new IllegalStateException("Source mode not yet supported:" + sourceMode);
+                        throw new AggregationException("Source mode not supported: " + sourceMode);
                 }
                 if (source != null){
                     descriptiveResultHolder.sources.add(source);
index 097e9095af62757e2d5c6027cd7cb5f4e6d12ee2..cfc59bafae196548ddb4a3563bdd17379c6ee6fb 100644 (file)
@@ -361,13 +361,19 @@ public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegr
         config.setWithinTaxonSourceMode(AggregationSourceMode.ALL);
         config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
         result = engine.invoke(config, repository);
-        verifyStatusNotOk(result);
+        verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL");
         commitAndStartNewTransaction();
 
-        config.setWithinTaxonSourceMode(AggregationSourceMode.ALL);
+        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
+        config.setToParentSourceMode(AggregationSourceMode.ALL);
+        result = engine.invoke(config, repository);
+        verifyStatusError(result, "Unsupported source mode for to-parent aggregation: ALL");
+        commitAndStartNewTransaction();
+
+        config.setWithinTaxonSourceMode(AggregationSourceMode.ALL_SAMEVALUE);
         config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
         result = engine.invoke(config, repository);
-        verifyStatusNotOk(result);
+        verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL_SAMEVALUE");
         commitAndStartNewTransaction();
 
 //        removeSomeDataFromFirstAggregation();
@@ -418,10 +424,18 @@ public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegr
         }
     }
 
-    private void verifyStatusNotOk(UpdateResult result) {
-        if (result.getStatus() == UpdateResult.Status.OK){
-            Assert.fail("Aggregation should fail but did not " + result.toString());
-            //TODO test for expected exception
+    private void verifyStatusError(UpdateResult result, String expectedMessage) {
+        if (result.getStatus() != UpdateResult.Status.ERROR){
+            Assert.fail("Aggregation should fail with status error " + result.toString());
+        }
+        if (result.getExceptions().isEmpty()){
+            Assert.fail("Failing aggregation include exception " + result.toString());
+        }
+        Exception e = result.getExceptions().iterator().next();
+        if (! (e instanceof AggregationException)){
+            Assert.fail("Exception should be of type AggregationException " + result.toString());
+        }else if (expectedMessage != null){
+            Assert.assertEquals(expectedMessage, e.getMessage());
         }
     }