Skip to content

Commit

Permalink
Resolve configured directories relative to a specific location
Browse files Browse the repository at this point in the history
Previously we resolved relative to the working directory which made it
hard to manipulate this in tests and may have been unexpected for
embedded users. Now we use a specific location, provided by the internal
unsupported.dbms.directories.neo4j_home setting.

neo4j_home is set to the working directory for server, but the database
directory when running embedded or in the desktop application.
  • Loading branch information
benbc committed Apr 7, 2016
1 parent 9af30da commit a20fa39
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 91 deletions.
Expand Up @@ -86,9 +86,8 @@ public class BoltKernelExtension extends KernelExtensionFactory<BoltKernelExtens
public static class Settings public static class Settings
{ {
@Description( "Directory for storing certificates to be used by Neo4j for TLS connections" ) @Description( "Directory for storing certificates to be used by Neo4j for TLS connections" )
public static Setting<File> certificates_directory = setting( public static Setting<File> certificates_directory =
"dbms.directories.certificates", PATH, "certificates" pathSetting( "dbms.directories.certificates", "certificates" );
);


@Internal @Internal
@Description( "Path to the X.509 public certificate to be used by Neo4j for TLS connections" ) @Description( "Path to the X.509 public certificate to be used by Neo4j for TLS connections" )
Expand Down
Expand Up @@ -24,20 +24,25 @@
import org.neo4j.graphdb.config.Setting; import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.factory.Description; import org.neo4j.graphdb.factory.Description;
import org.neo4j.kernel.configuration.Internal; import org.neo4j.kernel.configuration.Internal;
import org.neo4j.kernel.configuration.Settings;
import static org.neo4j.kernel.configuration.Settings.PATH;
import static org.neo4j.kernel.configuration.Settings.STRING;
import static org.neo4j.kernel.configuration.Settings.derivedSetting;
import static org.neo4j.kernel.configuration.Settings.pathSetting;
import static org.neo4j.kernel.configuration.Settings.setting;


public interface DatabaseManagementSystemSettings public interface DatabaseManagementSystemSettings
{ {
@Description("Name of the database to load") @Description("Name of the database to load")
Setting<String> active_database = Settings.setting( "dbms.active_database", Settings.STRING, "graph.db" ); Setting<String> active_database = setting( "dbms.active_database", STRING, "graph.db" );


@Description("Path of the data directory. You must not configure more than one Neo4j installation to use the " + @Description("Path of the data directory. You must not configure more than one Neo4j installation to use the " +
"same data directory.") "same data directory.")
Setting<File> data_directory = Settings.setting( "dbms.directories.data", Settings.PATH, "data" ); Setting<File> data_directory = pathSetting( "dbms.directories.data", "data" );


@Internal @Internal
Setting<File> database_path = Settings.derivedSetting( "unsupported.dbms.directories.database", Setting<File> database_path = derivedSetting( "unsupported.dbms.directories.database",
data_directory, active_database, data_directory, active_database,
( data, current ) -> new File( new File( data, "databases" ), current ), ( data, current ) -> new File( new File( data, "databases" ), current ),
Settings.PATH ); PATH );
} }
Expand Up @@ -32,12 +32,11 @@


public class DatabaseManagementSystemSettingsTest public class DatabaseManagementSystemSettingsTest
{ {

@Test @Test
public void shouldPutDatabaseDirectoriesIntoDataDatabases() public void shouldPutDatabaseDirectoriesIntoDataDatabases()
{ {
Config config = new Config( stringMap( DatabaseManagementSystemSettings.data_directory.name(), "the-data-directory" ) ); Config config = new Config( stringMap( DatabaseManagementSystemSettings.data_directory.name(), "the-data-directory" ) );
assertThat( config.get( DatabaseManagementSystemSettings.database_path ), assertThat( config.get( DatabaseManagementSystemSettings.database_path ),
equalTo( new File( System.getProperty("user.dir") + "/the-data-directory/databases/graph.db" ) ) ); equalTo( new File( "the-data-directory/databases/graph.db" ) ) );
} }
} }
Expand Up @@ -63,6 +63,7 @@
import static org.neo4j.kernel.configuration.Settings.max; import static org.neo4j.kernel.configuration.Settings.max;
import static org.neo4j.kernel.configuration.Settings.min; import static org.neo4j.kernel.configuration.Settings.min;
import static org.neo4j.kernel.configuration.Settings.options; import static org.neo4j.kernel.configuration.Settings.options;
import static org.neo4j.kernel.configuration.Settings.pathSetting;
import static org.neo4j.kernel.configuration.Settings.setting; import static org.neo4j.kernel.configuration.Settings.setting;


/** /**
Expand All @@ -74,6 +75,12 @@ public abstract class GraphDatabaseSettings
@Migrator @Migrator
private static final ConfigurationMigrator migrator = new GraphDatabaseConfigurationMigrator(); private static final ConfigurationMigrator migrator = new GraphDatabaseConfigurationMigrator();


@Internal
@Description("Root relative to which directory settings are resolved. This is set in code and should never be " +
"configured explicitly.")
public static final Setting<File> neo4j_home =
setting( "unsupported.dbms.directories.neo4j_home", PATH, NO_DEFAULT );

@Title("Read only database") @Title("Read only database")
@Description("Only allow read operations from this Neo4j instance. " + @Description("Only allow read operations from this Neo4j instance. " +
"This mode still requires write access to the directory for lock purposes.") "This mode still requires write access to the directory for lock purposes.")
Expand Down Expand Up @@ -164,7 +171,7 @@ public abstract class GraphDatabaseSettings


@Description( "Sets the root directory for file URLs used with the Cypher `LOAD CSV` clause. This must be set to a single " @Description( "Sets the root directory for file URLs used with the Cypher `LOAD CSV` clause. This must be set to a single "
+ "directory, restricting access to only those files within that directory and its subdirectories." ) + "directory, restricting access to only those files within that directory and its subdirectories." )
public static Setting<File> load_csv_file_url_root = setting( "dbms.directories.import", PATH, NO_DEFAULT ); public static Setting<File> load_csv_file_url_root = pathSetting( "dbms.directories.import", NO_DEFAULT );


@Description( "The maximum amount of time to wait for the database to become available, when " + @Description( "The maximum amount of time to wait for the database to become available, when " +
"starting a new transaction." ) "starting a new transaction." )
Expand All @@ -180,7 +187,7 @@ public abstract class GraphDatabaseSettings


@Description("Location of the database plugin directory. Compiled Java JAR files that contain database " + @Description("Location of the database plugin directory. Compiled Java JAR files that contain database " +
"procedures will be loaded if they are placed in this directory.") "procedures will be loaded if they are placed in this directory.")
public static final Setting<File> plugin_dir = setting("dbms.directories.plugins", PATH, "plugins" ); public static final Setting<File> plugin_dir = pathSetting( "dbms.directories.plugins", "plugins" );


@Description( "Threshold for rotation of the debug log." ) @Description( "Threshold for rotation of the debug log." )
public static final Setting<Long> store_internal_log_rotation_threshold = setting("dbms.logs.debug.rotation.size", BYTES, "20m", min(0L), max( Long.MAX_VALUE ) ); public static final Setting<Long> store_internal_log_rotation_threshold = setting("dbms.logs.debug.rotation.size", BYTES, "20m", min(0L), max( Long.MAX_VALUE ) );
Expand Down Expand Up @@ -420,7 +427,7 @@ private static String defaultPageCacheMemory()
public static final Setting<Boolean> log_queries = setting("dbms.logs.query.enabled", BOOLEAN, FALSE ); public static final Setting<Boolean> log_queries = setting("dbms.logs.query.enabled", BOOLEAN, FALSE );


@Description("Path of the logs directory") @Description("Path of the logs directory")
public static final Setting<File> logs_directory = setting("dbms.directories.logs", PATH, "logs"); public static final Setting<File> logs_directory = pathSetting( "dbms.directories.logs", "logs" );


@Internal @Internal
public static final Setting<File> log_queries_filename = derivedSetting("dbms.querylog.filename", public static final Setting<File> log_queries_filename = derivedSetting("dbms.querylog.filename",
Expand Down Expand Up @@ -451,7 +458,8 @@ private static String defaultPageCacheMemory()
public static final Setting<Boolean> auth_enabled = setting( "dbms.security.auth_enabled", BOOLEAN, "false" ); public static final Setting<Boolean> auth_enabled = setting( "dbms.security.auth_enabled", BOOLEAN, "false" );


@Internal @Internal
public static final Setting<File> auth_store = setting("unsupported.dbms.security.auth_store.location", PATH, NO_DEFAULT); public static final Setting<File> auth_store =
pathSetting( "unsupported.dbms.security.auth_store.location", NO_DEFAULT );


// Bolt Settings // Bolt Settings


Expand Down
Expand Up @@ -33,13 +33,15 @@
import org.neo4j.graphdb.config.Configuration; import org.neo4j.graphdb.config.Configuration;
import org.neo4j.graphdb.config.InvalidSettingException; import org.neo4j.graphdb.config.InvalidSettingException;
import org.neo4j.graphdb.config.Setting; import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.HostnamePort; import org.neo4j.helpers.HostnamePort;
import org.neo4j.helpers.TimeUtil; import org.neo4j.helpers.TimeUtil;
import org.neo4j.helpers.collection.Iterables; import org.neo4j.helpers.collection.Iterables;
import org.neo4j.io.fs.FileUtils;


import static java.lang.Character.isDigit; import static java.lang.Character.isDigit;


import static org.neo4j.io.fs.FileUtils.fixSeparatorsInPath;

/** /**
* Create settings for configurations in Neo4j. See {@link org.neo4j.graphdb.factory.GraphDatabaseSettings} for example. * Create settings for configurations in Neo4j. See {@link org.neo4j.graphdb.factory.GraphDatabaseSettings} for example.
* *
Expand Down Expand Up @@ -234,6 +236,56 @@ public OUT apply( Function<String, String> config )
}; };
} }


public static Setting<File> pathSetting( String name, String defaultValue )
{
return new Setting<File>()
{
@Override
public String name()
{
return name;
}

@Override
public String getDefaultValue()
{
return defaultValue;
}

@Override
public File from( Configuration config )
{
return config.get( this );
}

@Override
public File apply( Function<String, String> config )
{
String value = config.apply( name );
if ( value == null )
{
value = defaultValue;
}
if (value == null)
{
return null;
}

String setting = fixSeparatorsInPath( value );
File settingFile = new File( setting );

if ( settingFile.isAbsolute() )
{
return settingFile;
}
else
{
return new File( GraphDatabaseSettings.neo4j_home.apply( config ), setting );
}
}
};
}

private static <T> Function<Function<String, String>, String> inheritedValue( final Function<Function<String, private static <T> Function<Function<String, String>, String> inheritedValue( final Function<Function<String,
String>, String> lookup, final Setting<T> inheritedSetting ) String>, String> lookup, final Setting<T> inheritedSetting )
{ {
Expand Down Expand Up @@ -545,12 +597,12 @@ public String toString()
@Override @Override
public File apply( String setting ) public File apply( String setting )
{ {
setting = FileUtils.fixSeparatorsInPath( setting ); File file = new File( fixSeparatorsInPath( setting ) );

if ( !file.isAbsolute() )
File settingFile = new File( setting ); {
return settingFile.isAbsolute() ? throw new IllegalArgumentException( "Paths must be absolute. Got " + file );
settingFile : }
new File( System.getProperty( "user.dir" ), setting ); return file;
} }


@Override @Override
Expand Down
Expand Up @@ -21,6 +21,7 @@


import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
Expand Down Expand Up @@ -120,6 +121,12 @@ public DependencyResolver get()
life = dependencies.satisfyDependency( createLife() ); life = dependencies.satisfyDependency( createLife() );
this.graphDatabaseFacade = dependencies.satisfyDependency( graphDatabaseFacade ); this.graphDatabaseFacade = dependencies.satisfyDependency( graphDatabaseFacade );


if ( !params.containsKey( GraphDatabaseSettings.neo4j_home.name() ) )
{
params = new HashMap<>( params );
params.put( GraphDatabaseSettings.neo4j_home.name(), providedStoreDir.getAbsolutePath() );
}

// SPI - provided services // SPI - provided services
config = dependencies.satisfyDependency( new Config( params, getSettingsClasses( config = dependencies.satisfyDependency( new Config( params, getSettingsClasses(
externalDependencies.settingsClasses(), externalDependencies.kernelExtensions() ) ) ); externalDependencies.settingsClasses(), externalDependencies.kernelExtensions() ) ) );
Expand Down
Expand Up @@ -23,32 +23,55 @@


import org.junit.Test; import org.junit.Test;


import org.neo4j.graphdb.config.Setting;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

import static org.neo4j.kernel.configuration.Settings.NO_DEFAULT;
import static org.neo4j.kernel.configuration.Settings.pathSetting;


public class SettingsTest { public class SettingsTest
{
@Test @Test
public void makePathsAbsolute() public void parsesAbsolutePaths()
{ {
File thePath = Settings.PATH.apply( "foobar" ); File absolutePath = new File( "some/path" ).getAbsoluteFile();
File thePath = Settings.PATH.apply( absolutePath.toString() );


assertTrue( thePath.isAbsolute() ); assertEquals( absolutePath, thePath );
} }


@Test @Test
public void makePathInCurrentWorkingDirectory() public void doesntAllowRelativePaths()
{ {
File thePath = Settings.PATH.apply( "foobar" ); File relativePath = new File( "some/path" );

try
assertEquals( System.getProperty("user.dir"), thePath.getParent() ); {
Settings.PATH.apply( relativePath.toString() );
fail( "Expected an exception" );
}
catch ( IllegalArgumentException e )
{
// expected
}
} }


@Test @Test
public void doNotModifyAbsolutePaths() public void pathSettingsProvideDefaultValues()
{ {
File absolutePath = new File( "some/path" ).getAbsoluteFile(); File theDefault = new File( "/some/path" ).getAbsoluteFile();
File thePath = Settings.PATH.apply( absolutePath.toString() ); Setting<File> setting = pathSetting( "some.setting", theDefault.getAbsolutePath() );
assertThat( setting.from( Config.empty() ), is( theDefault ) );
}


assertEquals( absolutePath, thePath ); @Test
public void pathSettingsAreNullIfThereIsNoValueAndNoDefault()
{
Setting<File> setting = pathSetting( "some.setting", NO_DEFAULT );
assertThat( setting.from( Config.empty() ), is( nullValue() ) );
} }
} }
Expand Up @@ -34,7 +34,6 @@
import static org.neo4j.kernel.configuration.Settings.BOOLEAN; import static org.neo4j.kernel.configuration.Settings.BOOLEAN;
import static org.neo4j.kernel.configuration.Settings.INTEGER; import static org.neo4j.kernel.configuration.Settings.INTEGER;
import static org.neo4j.kernel.configuration.Settings.NO_DEFAULT; import static org.neo4j.kernel.configuration.Settings.NO_DEFAULT;
import static org.neo4j.kernel.configuration.Settings.PATH;
import static org.neo4j.kernel.configuration.Settings.STRING; import static org.neo4j.kernel.configuration.Settings.STRING;
import static org.neo4j.kernel.configuration.Settings.setting; import static org.neo4j.kernel.configuration.Settings.setting;


Expand Down Expand Up @@ -150,7 +149,7 @@ public void shouldDocumentBasicSettingsClass() throws Throwable
"[cols=\"<1h,<4\"]%n" + "[cols=\"<1h,<4\"]%n" +
"|===%n" + "|===%n" +
"|Description a|Public nodefault.%n" + "|Description a|Public nodefault.%n" +
"|Valid values a|public.nodefault is a path%n" + "|Valid values a|public.nodefault is a string%n" +
"|===%n" + "|===%n" +
"endif::nonhtmloutput[]%n" + "endif::nonhtmloutput[]%n" +
"%n" + "%n" +
Expand All @@ -160,7 +159,7 @@ public void shouldDocumentBasicSettingsClass() throws Throwable
"[cols=\"<1h,<4\"]%n" + "[cols=\"<1h,<4\"]%n" +
"|===%n" + "|===%n" +
"|Description a|Public nodefault.%n" + "|Description a|Public nodefault.%n" +
"|Valid values a|public.nodefault is a path%n" + "|Valid values a|public.nodefault is a string%n" +
"|===%n" + "|===%n" +
"endif::nonhtmloutput[]%n%n" ) )); "endif::nonhtmloutput[]%n%n" ) ));
} }
Expand Down Expand Up @@ -243,7 +242,7 @@ public void shouldDocumentGroupConfiguration() throws Throwable
public interface SimpleSettings public interface SimpleSettings
{ {
@Description("Public nodefault") @Description("Public nodefault")
Setting<File> public_nodefault = setting("public.nodefault", PATH, NO_DEFAULT); Setting<String> public_nodefault = setting( "public.nodefault", STRING, NO_DEFAULT );


@Description("Public with default") @Description("Public with default")
Setting<Integer> public_with_default = setting("public.default", INTEGER, "1"); Setting<Integer> public_with_default = setting("public.default", INTEGER, "1");
Expand Down
Expand Up @@ -19,8 +19,8 @@
*/ */
package org.neo4j.server; package org.neo4j.server;


import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;


import org.neo4j.dbms.DatabaseManagementSystemSettings; import org.neo4j.dbms.DatabaseManagementSystemSettings;
import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.factory.GraphDatabaseSettings;
Expand All @@ -44,7 +44,7 @@ protected NeoServer createNeoServer( Config config, GraphDatabaseDependencies de
} }


@Override @Override
protected Iterable<Class<?>> settingsClasses( HashMap<String, String> settings ) protected Iterable<Class<?>> settingsClasses( Map<String, String> settings )
{ {
return settingsClasses; return settingsClasses;
} }
Expand Down
Expand Up @@ -21,7 +21,7 @@


import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
Expand Down Expand Up @@ -138,7 +138,7 @@ public NeoServer getServer()
protected abstract NeoServer createNeoServer( Config config, GraphDatabaseDependencies dependencies, protected abstract NeoServer createNeoServer( Config config, GraphDatabaseDependencies dependencies,
LogProvider userLogProvider ); LogProvider userLogProvider );


protected abstract Iterable<Class<?>> settingsClasses( HashMap<String, String> settings ); protected abstract Iterable<Class<?>> settingsClasses( Map<String, String> settings );


private static LogProvider setupLogging() private static LogProvider setupLogging()
{ {
Expand Down
Expand Up @@ -25,7 +25,6 @@


import org.neo4j.helpers.Args; import org.neo4j.helpers.Args;
import org.neo4j.helpers.collection.Pair; import org.neo4j.helpers.collection.Pair;
import org.neo4j.kernel.configuration.Settings;
import org.neo4j.kernel.impl.util.Converters; import org.neo4j.kernel.impl.util.Converters;
import org.neo4j.server.configuration.ConfigLoader; import org.neo4j.server.configuration.ConfigLoader;


Expand Down Expand Up @@ -67,7 +66,7 @@ public Pair<String, String>[] configOverrides()
public Optional<File> configFile() public Optional<File> configFile()
{ {
return Optional.ofNullable( args.get( CONFIG_DIR_ARG ) ) return Optional.ofNullable( args.get( CONFIG_DIR_ARG ) )
.map( (dirPath) -> new File( Settings.PATH.apply( dirPath ), ConfigLoader.DEFAULT_CONFIG_FILE_NAME ) ); .map( (dirPath) -> new File( dirPath, ConfigLoader.DEFAULT_CONFIG_FILE_NAME ) );
} }


private static Pair<String, String>[] parseConfigOverrides( Args arguments ) private static Pair<String, String>[] parseConfigOverrides( Args arguments )
Expand Down

0 comments on commit a20fa39

Please sign in to comment.