diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeSuspendable.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeSuspendable.java index 2a5a066230c3b..435c2abbee5af 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeSuspendable.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeSuspendable.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import org.neo4j.function.ThrowingConsumer; @@ -47,19 +48,8 @@ public void disable() private void doOperation( ThrowingConsumer operation, String description ) { - try ( ErrorHandler errorHandler = new ErrorHandler( description ) ) - { - for ( Suspendable suspendable : suspendables ) - { - try - { - operation.accept( suspendable ); - } - catch ( Throwable throwable ) - { - errorHandler.add( throwable ); - } - } - } + ErrorHandler.certainOperations( description, suspendables.stream() + .map( (Function) suspendable -> () -> operation.accept( suspendable ) ) + .toArray( ErrorHandler.ThrowingRunnable[]::new ) ); } } diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/ErrorHandler.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/ErrorHandler.java index 99c40c960948d..99ffb970318cf 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/ErrorHandler.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/ErrorHandler.java @@ -22,11 +22,32 @@ import java.util.ArrayList; import java.util.List; +import org.neo4j.function.ThrowingAction; + + public class ErrorHandler implements AutoCloseable { private final List throwables = new ArrayList<>(); private final String message; + public static void certainOperations( String description, ThrowingRunnable... actions ) throws RuntimeException + { + try ( ErrorHandler errorHandler = new ErrorHandler( description ) ) + { + for ( ThrowingRunnable action : actions ) + { + try + { + action.run(); + } + catch ( Throwable e ) + { + errorHandler.add( e ); + } + } + } + } + public ErrorHandler( String message ) { this.message = message; @@ -62,4 +83,9 @@ private void throwIfException() throw runtimeException; } } + + public interface ThrowingRunnable + { + void run() throws Throwable; + } } diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/helper/CompositeSuspendableTest.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/helper/CompositeSuspendableTest.java new file mode 100644 index 0000000000000..7e9d25d62410c --- /dev/null +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/helper/CompositeSuspendableTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.helper; + +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class CompositeSuspendableTest +{ + + @Test + public void shouldEnableAllAndDisableAllEvenIfTheyThrow() + { + AtomicInteger count = new AtomicInteger(); + CompositeSuspendable compositeSuspendable = new CompositeSuspendable(); + int amountOfSuspendable = 3; + for ( int i = 0; i < amountOfSuspendable; i++ ) + { + compositeSuspendable.add( getSuspendable( count ) ); + } + + try + { + compositeSuspendable.enable(); + fail(); + } + catch ( RuntimeException ignore ) + { + + } + + assertEquals( amountOfSuspendable, count.get() ); + + try + { + compositeSuspendable.disable(); + fail(); + } + catch ( RuntimeException ignore ) + { + + } + + assertEquals( 0, count.get() ); + } + + private Suspendable getSuspendable( AtomicInteger count ) + { + return new Suspendable() + { + @Override + public void enable() + { + count.incrementAndGet(); + fail(); + } + + @Override + public void disable() + { + count.decrementAndGet(); + fail(); + } + }; + } +} diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/helper/ErrorHandlerTest.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/helper/ErrorHandlerTest.java new file mode 100644 index 0000000000000..20ec62197967b --- /dev/null +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/helper/ErrorHandlerTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.helper; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class ErrorHandlerTest +{ + private static final String FAILMESSAGE = "More fail"; + + @Test + public void shouldExecuteAllFailingOperations() + { + AtomicBoolean bool = new AtomicBoolean( false ); + try + { + ErrorHandler.certainOperations( "test", Assert::fail, () -> + { + bool.set( true ); + throw new IllegalStateException( FAILMESSAGE ); + } ); + fail(); + } + catch ( RuntimeException e ) + { + assertEquals( "test", e.getMessage() ); + Throwable cause = e.getCause(); + assertEquals( AssertionError.class, cause.getClass() ); + Throwable[] suppressed = e.getSuppressed(); + assertEquals( 1, suppressed.length ); + assertEquals( IllegalStateException.class, suppressed[0].getClass() ); + assertEquals( "More fail", suppressed[0].getMessage() ); + assertTrue( bool.get() ); + } + } +}