Skip to content

Commit

Permalink
Introduce Runnables.runAll
Browse files Browse the repository at this point in the history
Much like IOUtils it run all runnables, catch and chain any exceptions
and re throw them in the end wrapped in a RuntimeException.
  • Loading branch information
burqen committed Mar 7, 2019
1 parent cf06935 commit a0de978
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,37 @@
*/
package org.neo4j.util.concurrent;

import org.neo4j.helpers.Exceptions;

public class Runnables
{
public static final Runnable EMPTY_RUNNABLE = () ->
{
// empty
};

/**
* Run all runnables, chaining exceptions, if any, into a single {@link RuntimeException} with provided message as message.
* @param message passed to resulting {@link RuntimeException} if any runnable throw.
* @param runnables to run.
*/
public static void runAll( String message, Runnable... runnables )
{
Throwable exceptions = null;
for ( Runnable runnable : runnables )
{
try
{
runnable.run();
}
catch ( Throwable t )
{
exceptions = Exceptions.chain( exceptions, t );
}
}
if ( exceptions != null )
{
throw new RuntimeException( message, exceptions );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.util.concurrent;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.neo4j.helpers.Exceptions;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class RunnablesTest
{
@Test
void runAllMustRunAll()
{
// given
Task task1 = new Task();
Task task2 = new Task();
Task task3 = new Task();

// when
Runnables.runAll( "", task1, task2, task3 );

// then
assertRun( task1, task2, task3 );
}

@Test
void runAllMustRunAllAndPropagateError()
{
// given
Task task1 = new Task();
Task task2 = new Task();
Task task3 = new Task();
Error expectedError = new Error( "Killroy was here" );
Runnable throwingTask = error( expectedError );

List<Runnable> runnables = Arrays.asList( task1, task2, task3, throwingTask );
Collections.shuffle(runnables );

// when
String failureMessage = "Something wrong, Killroy must be here somewhere.";
RuntimeException actual = assertThrows( RuntimeException.class, () -> Runnables.runAll( failureMessage, runnables.toArray( new Runnable[0] ) ) );

// then
assertRun( task1, task2, task3 );
assertTrue( Exceptions.findCauseOrSuppressed( actual, t -> t == expectedError ).isPresent() );
assertEquals( failureMessage, actual.getMessage() );
}

@Test
void runAllMustRunAllAndPropagateMultipleErrors()
{
// given
Task task1 = new Task();
Task task2 = new Task();
Task task3 = new Task();
Error expectedError = new Error( "Killroy was here" );
Runnable throwingTask1 = error( expectedError );
RuntimeException expectedException = new RuntimeException( "and here" );
Runnable throwingTask2 = runtimeException( expectedException );

List<Runnable> runnables = Arrays.asList( task1, task2, task3, throwingTask1, throwingTask2 );
Collections.shuffle(runnables );

// when
String failureMessage = "Something wrong, Killroy must be here somewhere.";
RuntimeException actual = assertThrows( RuntimeException.class, () -> Runnables.runAll( failureMessage, runnables.toArray( new Runnable[0] ) ) );

// then
assertRun( task1, task2, task3 );
assertTrue( Exceptions.findCauseOrSuppressed( actual, t -> t == expectedError ).isPresent() );
assertTrue( Exceptions.findCauseOrSuppressed( actual, t -> t == expectedException ).isPresent() );
assertEquals( failureMessage, actual.getMessage() );
}

private Runnable error( Error error )
{
return () ->
{
throw error;
};
}

private Runnable runtimeException( RuntimeException runtimeException )
{
return () ->
{
throw runtimeException;
};
}

private void assertRun( Task... tasks )
{
for ( Task task : tasks )
{
assertTrue( task.run, "didn't run all expected tasks" );
}
}

private class Task implements Runnable
{
private boolean run;

@Override
public void run()
{
run = true;
}
}
}

0 comments on commit a0de978

Please sign in to comment.