Skip to content

Commit

Permalink
Basic neo4j-admin dump/load commands
Browse files Browse the repository at this point in the history
  • Loading branch information
benbc committed Sep 7, 2016
1 parent 46c0ae0 commit b8cd457
Show file tree
Hide file tree
Showing 7 changed files with 535 additions and 0 deletions.
4 changes: 4 additions & 0 deletions community/dbms/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,9 @@
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2002-2016 "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 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.commandline.dbms;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.Function;

import org.neo4j.commandline.admin.AdminCommand;
import org.neo4j.commandline.admin.CommandFailed;
import org.neo4j.commandline.admin.IncorrectUsage;
import org.neo4j.commandline.admin.OutsideWorld;
import org.neo4j.dbms.DatabaseManagementSystemSettings;
import org.neo4j.dbms.archive.Dumper;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.Args;
import org.neo4j.server.configuration.ConfigLoader;

import static java.util.Arrays.asList;

import static org.neo4j.dbms.DatabaseManagementSystemSettings.database_path;
import static org.neo4j.helpers.collection.MapUtil.stringMap;
import static org.neo4j.kernel.impl.util.Converters.mandatory;

public class DumpCommand implements AdminCommand
{
public static class Provider extends AdminCommand.Provider
{
public Provider()
{
super( "dump" );
}

@Override
public Optional<String> arguments()
{
return Optional.of( "" );
}

@Override
public String description()
{
return "";
}

@Override
public AdminCommand create( Path homeDir, Path configDir, OutsideWorld outsideWorld )
{
return new DumpCommand( homeDir, configDir, new Dumper() );
}
}

private final Path homeDir;
private final Path configDir;
private final Dumper dumper;

public DumpCommand( Path homeDir, Path configDir, Dumper dumper )
{
this.homeDir = homeDir;
this.configDir = configDir;
this.dumper = dumper;
}

@Override
public void execute( String[] args ) throws IncorrectUsage, CommandFailed
{
Path databaseDirectory = parse( args, "database", this::toDatabaseDirectory );
Path archive = parse( args, "to", Paths::get );
try
{
dumper.dump( databaseDirectory, archive );
}
catch ( IOException e )
{
throw new CommandFailed( "unable to dump database", e );
}
}

private <T> T parse( String[] args, String argument, Function<String, T> converter ) throws IncorrectUsage
{
try
{
return Args.parse( args ).interpretOption( argument, mandatory(), converter );
}
catch ( IllegalArgumentException e )
{
throw new IncorrectUsage( e.getMessage() );
}
}

private Path toDatabaseDirectory( String databaseName )
{
//noinspection unchecked
return new ConfigLoader( asList( DatabaseManagementSystemSettings.class, GraphDatabaseSettings.class ) )
.loadConfig(
Optional.of( homeDir.toFile() ),
Optional.of( configDir.resolve( "neo4j.conf" ).toFile() ) )
.with( stringMap( DatabaseManagementSystemSettings.active_database.name(), databaseName ) )
.get( database_path ).toPath();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2002-2016 "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 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.commandline.dbms;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.Function;

import org.neo4j.commandline.admin.AdminCommand;
import org.neo4j.commandline.admin.CommandFailed;
import org.neo4j.commandline.admin.IncorrectUsage;
import org.neo4j.commandline.admin.OutsideWorld;
import org.neo4j.dbms.DatabaseManagementSystemSettings;
import org.neo4j.dbms.archive.IncorrectFormat;
import org.neo4j.dbms.archive.Loader;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.Args;
import org.neo4j.server.configuration.ConfigLoader;

import static java.util.Arrays.asList;

import static org.neo4j.dbms.DatabaseManagementSystemSettings.database_path;
import static org.neo4j.helpers.collection.MapUtil.stringMap;
import static org.neo4j.kernel.impl.util.Converters.mandatory;

public class LoadCommand implements AdminCommand
{
public static class Provider extends AdminCommand.Provider
{
public Provider()
{
super( "load" );
}

@Override
public Optional<String> arguments()
{
return Optional.of( "" );
}

@Override
public String description()
{
return "";
}

@Override
public AdminCommand create( Path homeDir, Path configDir, OutsideWorld outsideWorld )
{
return new LoadCommand( homeDir, configDir, new Loader() );
}
}

private final Path homeDir;
private final Path configDir;
private final Loader loader;

public LoadCommand( Path homeDir, Path configDir, Loader loader )
{
this.homeDir = homeDir;
this.configDir = configDir;
this.loader = loader;
}

@Override
public void execute( String[] args ) throws IncorrectUsage, CommandFailed
{
Path archive = parse( args, "from", Paths::get );
Path databaseDirectory = parse( args, "database", this::toDatabaseDirectory );
try
{
loader.load( archive, databaseDirectory );
}
catch ( IOException e )
{
throw new CommandFailed( "unable to load database: " + e.getMessage(), e );
}
catch ( IncorrectFormat incorrectFormat )
{
throw new CommandFailed( "Not a valid Neo4j archive: " + archive, incorrectFormat );
}
}

private <T> T parse( String[] args, String argument, Function<String, T> converter ) throws IncorrectUsage
{
try
{
return Args.parse( args ).interpretOption( argument, mandatory(), converter );
}
catch ( IllegalArgumentException e )
{
throw new IncorrectUsage( e.getMessage() );
}
}

private Path toDatabaseDirectory( String databaseName )
{
//noinspection unchecked
return new ConfigLoader( asList( DatabaseManagementSystemSettings.class, GraphDatabaseSettings.class ) )
.loadConfig(
Optional.of( homeDir.toFile() ),
Optional.of( configDir.resolve( "neo4j.conf" ).toFile() ) )
.with( stringMap( DatabaseManagementSystemSettings.active_database.name(), databaseName ) )
.get( database_path ).toPath();
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
org.neo4j.commandline.dbms.ImportCommand$Provider
org.neo4j.commandline.dbms.DumpCommand$Provider
org.neo4j.commandline.dbms.LoadCommand$Provider
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2002-2016 "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 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.commandline.dbms;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import org.neo4j.commandline.admin.CommandFailed;
import org.neo4j.commandline.admin.IncorrectUsage;
import org.neo4j.dbms.archive.Dumper;
import org.neo4j.test.rule.TestDirectory;

import static java.util.Arrays.asList;

import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import static org.neo4j.dbms.DatabaseManagementSystemSettings.data_directory;

public class DumpCommandTest
{
@Rule
public TestDirectory testDirectory = TestDirectory.testDirectory();
private Path homeDir;
private Path configDir;
private Path archive;
private Dumper dumper;

@Before
public void setUp() throws Exception
{
homeDir = testDirectory.directory( "home-dir" ).toPath();
configDir = testDirectory.directory( "config-dir" ).toPath();
archive = testDirectory.directory( "some-archive.dump" ).toPath();
dumper = mock( Dumper.class );
}

@Test
public void shouldDumpTheDatabaseToTheArchive() throws CommandFailed, IncorrectUsage, IOException
{
execute( "foo.db" );
verify( dumper ).dump( homeDir.resolve( "data/databases/foo.db" ), archive );
}

@Test
public void shouldCalculateTheDatabaseDirectoryFromConfig() throws IOException, CommandFailed, IncorrectUsage
{
Files.write( configDir.resolve( "neo4j.conf" ), asList( data_directory.name() + "=/some/data/dir" ) );
execute( "foo.db" );
verify( dumper ).dump( eq( Paths.get( "/some/data/dir/databases/foo.db" ) ), any() );
}

@Test
public void shouldObjectIfTheDatabaseArgumentIsMissing() throws CommandFailed
{
try
{
new DumpCommand( null, null, null ).execute( new String[]{"--to=something"} );
fail( "expected exception" );
}
catch ( IncorrectUsage e )
{
assertThat( e.getMessage(), equalTo( "Missing argument 'database'" ) );
}
}

@Test
public void shouldObjectIfTheArchiveArgumentIsMissing() throws CommandFailed
{
try
{
new DumpCommand( homeDir, configDir, null ).execute( new String[]{"--database=something"} );
fail( "expected exception" );
}
catch ( IncorrectUsage e )
{
assertThat( e.getMessage(), equalTo( "Missing argument 'to'" ) );
}
}

@Test
public void shouldThrowIfTheCommandFails() throws IOException, IncorrectUsage
{
doThrow( IOException.class ).when( dumper ).dump( any(), any() );
try
{
execute( null );
fail( "expected exception" );
}
catch ( CommandFailed ignored )
{
}
}

private void execute( final String database ) throws IncorrectUsage, CommandFailed
{
new DumpCommand( homeDir, configDir, dumper )
.execute( new String[]{"--database=" + database, "--to=" + archive} );
}
}

0 comments on commit b8cd457

Please sign in to comment.