/** * AnalyzerBeans * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.eobjects.analyzer.test.full.scenarios; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.eobjects.analyzer.beans.api.Analyzer; import org.eobjects.analyzer.beans.api.AnalyzerBean; import org.eobjects.analyzer.beans.api.Close; import org.eobjects.analyzer.beans.api.Configured; import org.eobjects.analyzer.configuration.AnalyzerBeansConfiguration; import org.eobjects.analyzer.configuration.AnalyzerBeansConfigurationImpl; import org.eobjects.analyzer.connection.Datastore; import org.eobjects.analyzer.connection.DatastoreCatalogImpl; import org.eobjects.analyzer.data.InputColumn; import org.eobjects.analyzer.data.InputRow; import org.eobjects.analyzer.job.AnalysisJob; import org.eobjects.analyzer.job.builder.AnalysisJobBuilder; import org.eobjects.analyzer.job.concurrent.PreviousErrorsExistException; import org.eobjects.analyzer.job.runner.AnalysisJobFailedException; import org.eobjects.analyzer.job.runner.AnalysisResultFuture; import org.eobjects.analyzer.job.runner.AnalysisRunnerImpl; import org.eobjects.analyzer.result.NumberResult; import org.eobjects.analyzer.test.ActivityAwareMultiThreadedTaskRunner; import org.eobjects.analyzer.test.TestHelper; import org.eobjects.analyzer.util.CollectionUtils2; import org.eobjects.analyzer.util.SchemaNavigator; import org.apache.metamodel.schema.Column; /** * Tests that a job where one of the row processing consumers fail is gracefully * error handled. * * */ public class ErrorInRowProcessingConsumerTest extends TestCase { private static final AtomicBoolean closed = new AtomicBoolean(); @AnalyzerBean("Errornous analyzer") public static class ErrornousAnalyzer implements Analyzer<NumberResult> { private final AtomicInteger counter = new AtomicInteger(0); @Configured InputColumn<String> inputColumn; @Override public NumberResult getResult() { return new NumberResult(counter.get()); } @Override public void run(InputRow row, int distinctCount) { assertNotNull(inputColumn); assertNotNull(row); assertEquals(1, distinctCount); String value = row.getValue(inputColumn); assertNotNull(value); int count = counter.incrementAndGet(); if (count == 3) { throw new IllegalStateException("This analyzer can only analyze two rows!"); } } @Close public void close() { closed.set(true); } } public void testScenario() throws Exception { closed.set(false); ActivityAwareMultiThreadedTaskRunner taskRunner = new ActivityAwareMultiThreadedTaskRunner(); Datastore datastore = TestHelper.createSampleDatabaseDatastore("my db"); AnalyzerBeansConfiguration conf = new AnalyzerBeansConfigurationImpl().replace(taskRunner).replace( new DatastoreCatalogImpl(datastore)); AnalysisJob job; try (AnalysisJobBuilder ajb = new AnalysisJobBuilder(conf)) { ajb.setDatastore(datastore); SchemaNavigator schemaNavigator = datastore.openConnection().getSchemaNavigator(); Column column = schemaNavigator.convertToColumn("PUBLIC.EMPLOYEES.EMAIL"); assertNotNull(column); ajb.addSourceColumn(column); ajb.addAnalyzer(ErrornousAnalyzer.class).addInputColumn(ajb.getSourceColumns().get(0)); job = ajb.toAnalysisJob(); } AnalysisResultFuture resultFuture = new AnalysisRunnerImpl(conf).run(job); assertTrue(resultFuture.isErrornous()); // isErrornous should be blocking assertTrue(resultFuture.isDone()); try { resultFuture.getResults(); fail("Exception expected"); } catch (AnalysisJobFailedException e) { String message = e.getMessage(); assertEquals("The analysis ended with 2 errors: [" + "IllegalStateException: This analyzer can only analyze two rows!," + "PreviousErrorsExistException: A previous exception has occurred]", message); } List<Throwable> errors = resultFuture.getErrors(); // the amount of errors may vary depending on the thread scheduling int numErrors = errors.size(); assertTrue(numErrors == 2 || numErrors == 3); // sort the errors to make the order deterministic errors = CollectionUtils2.sorted(errors, new Comparator<Throwable>() { @Override public int compare(Throwable o1, Throwable o2) { return o1.getClass().getName().compareTo(o2.getClass().getName()); } }); assertEquals(IllegalStateException.class, errors.get(0).getClass()); assertEquals("This analyzer can only analyze two rows!", errors.get(0).getMessage()); assertTrue(numErrors + " errors found, 2 or 3 expected!", numErrors == 2 || numErrors == 3); if (numErrors == 3) { // this is caused by the assertion // ("assertEquals(1, distinctCount);") // above assertEquals(AssertionFailedError.class, errors.get(1).getClass()); assertEquals("expected:<1> but was:<2>", errors.get(1).getMessage()); assertEquals(PreviousErrorsExistException.class, errors.get(2).getClass()); assertEquals("A previous exception has occurred", errors.get(2).getMessage()); } else { assertEquals(PreviousErrorsExistException.class, errors.get(1).getClass()); assertEquals("A previous exception has occurred", errors.get(1).getMessage()); } int taskCount = taskRunner.assertAllBegunTasksFinished(500); assertTrue("taskCount was: " + taskCount, taskCount > 4); assertTrue(closed.get()); } }