Skip to content

Commit

Permalink
Generalize the blockers to AdminTool.
Browse files Browse the repository at this point in the history
  • Loading branch information
srbaker committed Oct 28, 2016
1 parent 6326953 commit 819f6c0
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 134 deletions.
Expand Up @@ -25,7 +25,6 @@

import org.neo4j.helpers.Service;
import org.neo4j.helpers.collection.Iterables;
import org.neo4j.kernel.configuration.Config;

/**
* To create a command for {@code neo4j-admin}:
Expand Down Expand Up @@ -78,29 +77,24 @@ public String name()
public abstract AdminCommand create( Path homeDir, Path configDir, OutsideWorld outsideWorld );
}

abstract class Blocker extends Service
interface Blocker
{
protected Blocker( String key, String... altKeys )
{
super( key, altKeys );
}

/**
* @param databaseName the name of the database that the command applies to.
* @param config a configuration object for the blocker to use when making its decision.
* @return Whether the command should be blocked from running or not.
* @param homeDir the home of the Neo4j installation.
* @param configDir the directory where configuration files can be found.
* @return A boolean representing whether or not this command should be blocked from running.
*/
public abstract boolean doesBlock( String databaseName, Config config );
boolean doesBlock( Path homeDir, Path configDir );

/**
* @return a list of the commands this blocker applies to.
* @return A list of the commands this blocker applies to.
*/
public abstract Set<String> commands();
Set<String> commands();

/**
* @return explanation of why a command was blocked. This will be shown to the user.
* @return An explanation of why a command was blocked. This will be shown to the user.
*/
public abstract String explanation();
String explanation();
}

void execute( String[] args ) throws IncorrectUsage, CommandFailed;
Expand Down
Expand Up @@ -35,22 +35,25 @@ public static void main( String[] args )
Path configDir = Paths.get( System.getenv().getOrDefault( "NEO4J_CONF", "" ) );
boolean debug = System.getenv( "NEO4J_DEBUG" ) != null;

new AdminTool( CommandLocator.fromServiceLocator(), new RealOutsideWorld(), debug )
.execute( homeDir, configDir, args );
new AdminTool( CommandLocator.fromServiceLocator(), BlockerLocator.fromServiceLocator(), new RealOutsideWorld(),
debug ).execute( homeDir, configDir, args );
}

private final String scriptName = "neo4j-admin";
private final CommandLocator locator;
private final CommandLocator commandLocator;
private final BlockerLocator blockerLocator;
private final OutsideWorld outsideWorld;
private final boolean debug;
private final Usage usage;

public AdminTool( CommandLocator locator, OutsideWorld outsideWorld, boolean debug )
public AdminTool( CommandLocator commandLocator, BlockerLocator blockerLocator, OutsideWorld outsideWorld,
boolean debug )
{
this.locator = CommandLocator.withAdditionalCommand( help(), locator );
this.commandLocator = CommandLocator.withAdditionalCommand( help(), commandLocator );
this.blockerLocator = blockerLocator;
this.outsideWorld = outsideWorld;
this.debug = debug;
this.usage = new Usage( scriptName, this.locator );
this.usage = new Usage( scriptName, this.commandLocator );
}

public void execute( Path homeDir, Path configDir, String... args )
Expand All @@ -68,7 +71,14 @@ public void execute( Path homeDir, Path configDir, String... args )
AdminCommand.Provider provider;
try
{
provider = locator.findProvider( name );
provider = commandLocator.findProvider( name );
for ( AdminCommand.Blocker blocker : blockerLocator.findBlockers( name ) )
{
if ( blocker.doesBlock( homeDir, configDir ) )
{
commandFailed( new CommandFailed( blocker.explanation() ) );
}
}
}
catch ( NoSuchElementException e )
{
Expand Down
Expand Up @@ -20,22 +20,27 @@
package org.neo4j.commandline.admin;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

import org.neo4j.helpers.Service;

public class BlockerLocator
public interface BlockerLocator
{
public List<AdminCommand.Blocker> findBlockersForCommand( String command )
Iterable<AdminCommand.Blocker> findBlockers( String name ) throws NoSuchElementException;

static BlockerLocator fromServiceLocator()
{
ArrayList<AdminCommand.Blocker> blockers = new ArrayList<>();
for ( AdminCommand.Blocker blocker : Service.load( AdminCommand.Blocker.class ) )
return commandName ->
{
if ( blocker.commands().contains( command ) )
ArrayList<AdminCommand.Blocker> blockers = new ArrayList<>();
for ( AdminCommand.Blocker blocker : Service.load( AdminCommand.Blocker.class ) )
{
blockers.add( blocker );
if ( blocker.commands().contains( commandName ) )
{
blockers.add( blocker );
}
}
}
return blockers;
return blockers;
};
}
}
Expand Up @@ -23,22 +23,27 @@
import org.mockito.InOrder;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.NoSuchElementException;
import java.util.Optional;

import org.neo4j.helpers.collection.Iterables;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class AdminToolTest
{
@Test
public void shouldExecuteTheCommand() throws CommandFailed, IncorrectUsage
{
AdminCommand command = mock( AdminCommand.class );
new AdminTool( cannedCommand( "command", command ), new NullOutsideWorld(), false )
new AdminTool( cannedCommand( "command", command ), new NullBlockerLocator(), new NullOutsideWorld(), false )
.execute( null, null, "command", "the", "other", "args" );
verify( command ).execute( new String[]{"the", "other", "args"} );
}
Expand All @@ -47,7 +52,7 @@ public void shouldExecuteTheCommand() throws CommandFailed, IncorrectUsage
public void shouldExit0WhenEverythingWorks()
{
OutsideWorld outsideWorld = mock( OutsideWorld.class );
new AdminTool( new CannedLocator( new NullCommandProvider() ), outsideWorld, false )
new AdminTool( new CannedLocator( new NullCommandProvider() ), new NullBlockerLocator(), outsideWorld, false )
.execute( null, null, "null" );
verify( outsideWorld ).exit( 0 );
}
Expand All @@ -56,15 +61,16 @@ public void shouldExit0WhenEverythingWorks()
public void shouldAddTheHelpCommandToThoseProvidedByTheLocator()
{
OutsideWorld outsideWorld = mock( OutsideWorld.class );
new AdminTool( new NullCommandLocator(), outsideWorld, false ).execute( null, null, "help" );
new AdminTool( new NullCommandLocator(), new NullBlockerLocator(), outsideWorld, false )
.execute( null, null, "help" );
verify( outsideWorld ).stdOutLine( " help" );
}

@Test
public void shouldProvideFeedbackWhenNoCommandIsProvided()
{
OutsideWorld outsideWorld = mock( OutsideWorld.class );
new AdminTool( new NullCommandLocator(), outsideWorld, false ).execute( null, null );
new AdminTool( new NullCommandLocator(), new NullBlockerLocator(), outsideWorld, false ).execute( null, null );
verify( outsideWorld ).stdErrLine( "you must provide a command" );
verify( outsideWorld ).stdErrLine( "Usage: neo4j-admin <command>" );
verify( outsideWorld ).exit( 1 );
Expand All @@ -78,7 +84,8 @@ public void shouldProvideFeedbackIfTheCommandThrowsARuntimeException()
{
throw new RuntimeException( "the-exception-message" );
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, false ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, false )
.execute( null, null, "exception" );
verify( outsideWorld ).stdErrLine( "unexpected error: the-exception-message" );
verify( outsideWorld ).exit( 1 );
}
Expand All @@ -92,7 +99,8 @@ public void shouldPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTheDe
{
throw exception;
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, true ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, true )
.execute( null, null, "exception" );
verify( outsideWorld ).printStacktrace( exception );
}

Expand All @@ -105,7 +113,8 @@ public void shouldNotPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTh
{
throw exception;
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, false ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, false )
.execute( null, null, "exception" );
verify( outsideWorld, never() ).printStacktrace( exception );
}

Expand All @@ -117,7 +126,8 @@ public void shouldProvideFeedbackIfTheCommandFails()
{
throw new CommandFailed( "the-failure-message" );
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, false ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, false )
.execute( null, null, "exception" );
verify( outsideWorld ).stdErrLine( "command failed: the-failure-message" );
verify( outsideWorld ).exit( 1 );
}
Expand All @@ -131,7 +141,8 @@ public void shouldPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsSet()
{
throw exception;
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, true ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, true )
.execute( null, null, "exception" );
verify( outsideWorld ).printStacktrace( exception );
}

Expand All @@ -144,7 +155,8 @@ public void shouldNotPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsNotSet
{
throw exception;
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, false ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, false )
.execute( null, null, "exception" );
verify( outsideWorld, never() ).printStacktrace( exception );
}

Expand All @@ -156,12 +168,78 @@ public void shouldProvideFeedbackIfTheCommandReportsAUsageProblem()
{
throw new IncorrectUsage( "the-usage-message" );
};
new AdminTool( cannedCommand( "exception", command ), outsideWorld, false ).execute( null, null, "exception" );
new AdminTool( cannedCommand( "exception", command ), new NullBlockerLocator(), outsideWorld, false ).execute(
null, null,
"exception" );
InOrder inOrder = inOrder( outsideWorld );
inOrder.verify( outsideWorld ).stdErrLine( "the-usage-message" );
verify( outsideWorld ).exit( 1 );
}

@Test
public void shouldBlockDumpIfABlockerSaysSo() throws IncorrectUsage
{
OutsideWorld outsideWorld = mock( OutsideWorld.class );
AdminCommand command = mock( AdminCommand.class );

AdminCommand.Blocker blocker = mock( AdminCommand.Blocker.class );
when( blocker.doesBlock( any(), any() ) ).thenReturn( true );
when( blocker.commands() ).thenReturn( Collections.singleton( "command" ) );
when( blocker.explanation() ).thenReturn( "the explanation" );

BlockerLocator blockerLocator = mock( BlockerLocator.class );
when( blockerLocator.findBlockers( "command" ) ).thenReturn( Collections.singletonList( blocker ) );

new AdminTool( cannedCommand( "command", command ), blockerLocator, outsideWorld, false )
.execute( null, null, "command" );

verify( outsideWorld ).stdErrLine( "command failed: the explanation" );
verify( outsideWorld ).exit( 1 );
}

@Test
public void shouldBlockDumpIfOneBlockerOutOfManySaysSo() throws IncorrectUsage
{
OutsideWorld outsideWorld = mock( OutsideWorld.class );
AdminCommand command = mock( AdminCommand.class );

AdminCommand.Blocker trueBlocker = mock( AdminCommand.Blocker.class );
when( trueBlocker.doesBlock( any(), any() ) ).thenReturn( true );
when( trueBlocker.explanation() ).thenReturn( "trueBlocker explanation" );

AdminCommand.Blocker falseBlocker = mock( AdminCommand.Blocker.class );
when( falseBlocker.doesBlock( any(), any() ) ).thenReturn( false );
when( falseBlocker.explanation() ).thenReturn( "falseBlocker explanation" );

BlockerLocator blockerLocator = mock( BlockerLocator.class );
when( blockerLocator.findBlockers( "command" ) )
.thenReturn( Arrays.asList( falseBlocker, trueBlocker, falseBlocker ) );

new AdminTool( cannedCommand( "command", command ), blockerLocator, outsideWorld, false )
.execute( null, null, "command" );

verify( outsideWorld ).stdErrLine( "command failed: trueBlocker explanation" );
verify( outsideWorld ).exit( 1 );
}

@Test
public void shouldNotBlockIfNoneOfTheBlockersBlock() throws CommandFailed, IncorrectUsage
{
AdminCommand command = mock( AdminCommand.class );

AdminCommand.Blocker falseBlocker = mock( AdminCommand.Blocker.class );
when( falseBlocker.doesBlock( any(), any() ) ).thenReturn( false );
when( falseBlocker.explanation() ).thenReturn( "falseBlocker explanation" );

BlockerLocator blockerLocator = mock( BlockerLocator.class );
when( blockerLocator.findBlockers( "command" ) )
.thenReturn( Arrays.asList( falseBlocker, falseBlocker, falseBlocker ) );

new AdminTool( cannedCommand( "command", command ), blockerLocator, new NullOutsideWorld(), false )
.execute( null, null, "command", "the", "other", "args" );
verify( command ).execute( new String[]{"the", "other", "args"} );
}

private CannedLocator cannedCommand( final String name, AdminCommand command )
{
return new CannedLocator( new AdminCommand.Provider( name )
Expand Down Expand Up @@ -240,4 +318,13 @@ public AdminCommand create( Path homeDir, Path configDir, OutsideWorld outsideWo
};
}
}

private static class NullBlockerLocator implements BlockerLocator
{
@Override
public Iterable<AdminCommand.Blocker> findBlockers( String name ) throws NoSuchElementException
{
return Collections.emptyList();
}
}
}

0 comments on commit 819f6c0

Please sign in to comment.