diff --git a/kernel/src/main/java/org/neo4j/kernel/impl/recovery/StoreRecoverer.java b/kernel/src/main/java/org/neo4j/kernel/impl/recovery/StoreRecoverer.java new file mode 100644 index 000000000..63fc634ae --- /dev/null +++ b/kernel/src/main/java/org/neo4j/kernel/impl/recovery/StoreRecoverer.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.kernel.impl.recovery; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.util.Map; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.kernel.DefaultFileSystemAbstraction; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction; +import org.neo4j.kernel.impl.transaction.xaframework.XaLogicalLogFiles; +import org.neo4j.kernel.impl.transaction.xaframework.XaLogicalLogRecoveryCheck; + +/** + * For now, an external tool that can determine if a given store + * will need recovery, and perform recovery on given stores. + */ +public class StoreRecoverer { + + private FileSystemAbstraction fs; + + public StoreRecoverer() + { + this(new DefaultFileSystemAbstraction()); + } + + public StoreRecoverer(FileSystemAbstraction fs) + { + this.fs = fs; + } + + public boolean recoveryNeededAt(File dataDir, Map params) throws IOException + { + // We need config to determine where the logical log files are + params.put(GraphDatabaseSettings.store_dir.name(), dataDir.getAbsolutePath()); + Config config = new Config(params, GraphDatabaseSettings.class); + + String baseLogPath = config.get(GraphDatabaseSettings.logical_log); + XaLogicalLogFiles logFiles = new XaLogicalLogFiles(baseLogPath, fs); + + String log; + switch(logFiles.determineState()) + { + case CLEAN: + return false; + + case NO_ACTIVE_FILE: + case DUAL_LOGS_LOG_1_ACTIVE: + case DUAL_LOGS_LOG_2_ACTIVE: + return true; + + case LEGACY_WITHOUT_LOG_ROTATION: + log = baseLogPath; + break; + + case LOG_1_ACTIVE: + log = logFiles.getLog1FileName(); + break; + + case LOG_2_ACTIVE: + log = logFiles.getLog2FileName(); + break; + + default: + return true; + } + + FileChannel logChannel = null; + try + { + logChannel = fs.open(log, "r"); + return new XaLogicalLogRecoveryCheck(logChannel).recoveryRequired(); + } finally + { + if(logChannel != null) + { + logChannel.close(); + } + } + } + + public void recover(File dataDir, Map params) throws IOException + { + // For now, just launch a full embedded database on top of the directory. + // In a perfect world, to be expanded to only do recovery, and to be used + // as a component of the database, rather than something that is bolted + // on outside it like this. + GraphDatabaseService db = + new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(dataDir.getCanonicalPath()) + .setConfig(params).newGraphDatabase(); + + db.shutdown(); + } + +} diff --git a/kernel/src/main/java/org/neo4j/kernel/impl/transaction/xaframework/XaLogicalLog.java b/kernel/src/main/java/org/neo4j/kernel/impl/transaction/xaframework/XaLogicalLog.java index 1b0063b61..915295405 100644 --- a/kernel/src/main/java/org/neo4j/kernel/impl/transaction/xaframework/XaLogicalLog.java +++ b/kernel/src/main/java/org/neo4j/kernel/impl/transaction/xaframework/XaLogicalLog.java @@ -133,7 +133,6 @@ public XaLogicalLog( String fileName, XaResourceManager xaRm, XaCommandFactory c synchronized void open() throws IOException { - switch(logFiles.determineState()) { case LEGACY_WITHOUT_LOG_ROTATION: @@ -1019,7 +1018,8 @@ public synchronized Pair getMasterForCommittedTransaction( long t * @return The channel * @throws IOException If an IO error occurs when reading the log file */ - public ReadableByteChannel getLogicalLogOrMyselfCommitted( long version, long position ) + @Override + public ReadableByteChannel getLogicalLogOrMyselfCommitted( long version, long position ) throws IOException { synchronized ( this ) @@ -1592,7 +1592,8 @@ public long getLogicalLogTargetSize() return this.rotateAtSize; } - public String getFileName( long version ) + @Override + public String getFileName( long version ) { return fileName + ".v" + version; } @@ -1640,7 +1641,8 @@ public boolean wasNonClean() return nonCleanShutdown; } - public long getHighestLogVersion() + @Override + public long getHighestLogVersion() { return logVersion; } diff --git a/kernel/src/test/java/org/neo4j/kernel/impl/recovery/TestStoreRecoverer.java b/kernel/src/test/java/org/neo4j/kernel/impl/recovery/TestStoreRecoverer.java new file mode 100644 index 000000000..c9c4ea3fa --- /dev/null +++ b/kernel/src/test/java/org/neo4j/kernel/impl/recovery/TestStoreRecoverer.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.kernel.impl.recovery; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; + +import org.junit.Test; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.kernel.impl.util.FileUtils; + + +public class TestStoreRecoverer { + + @Test + public void shouldNotWantToRecoverIntactStore() throws Exception + { + File store = null; + try + { + store = createIntactStore(); + + StoreRecoverer recoverer = new StoreRecoverer(); + + assertThat(recoverer.recoveryNeededAt(store, new HashMap()), is(false)); + + } finally + { + if(store != null) + { + FileUtils.deleteRecursively(store); + } + } + + } + + @Test + public void shouldWantToRecoverBrokenStore() throws Exception + { + File store = null; + try + { + store = createIntactStore(); + new File(store,"nioneo_logical.log.active").delete(); + + StoreRecoverer recoverer = new StoreRecoverer(); + + assertThat(recoverer.recoveryNeededAt(store, new HashMap()), is(true)); + + } finally + { + if(store != null) + { + FileUtils.deleteRecursively(store); + } + } + + } + + @Test + public void shouldBeAbleToRecoverBrokenStore() throws Exception + { + File store = null; + try + { + store = createIntactStore(); + new File(store,"nioneo_logical.log.active").delete(); + + StoreRecoverer recoverer = new StoreRecoverer(); + + assertThat(recoverer.recoveryNeededAt(store, new HashMap()), is(true)); + + recoverer.recover(store, new HashMap()); + + assertThat(recoverer.recoveryNeededAt(store, new HashMap()), is(false)); + + } finally + { + if(store != null) + { + FileUtils.deleteRecursively(store); + } + } + + } + + private File createIntactStore() throws IOException { + File tmpFile = File.createTempFile( "neo4j-test", "" ); + tmpFile.delete(); + GraphDatabaseService db = + new GraphDatabaseFactory().newEmbeddedDatabase(tmpFile.getCanonicalPath()); + + db.shutdown(); + return tmpFile; + } + +} diff --git a/server/src/functionaltest/java/org/neo4j/server/StartupHealthcheckFunctionalTest.java b/server/src/functionaltest/java/org/neo4j/server/PreflightTasksFunctionalTest.java similarity index 78% rename from server/src/functionaltest/java/org/neo4j/server/StartupHealthcheckFunctionalTest.java rename to server/src/functionaltest/java/org/neo4j/server/PreflightTasksFunctionalTest.java index b0fa195bc..b80d620c1 100644 --- a/server/src/functionaltest/java/org/neo4j/server/StartupHealthcheckFunctionalTest.java +++ b/server/src/functionaltest/java/org/neo4j/server/PreflightTasksFunctionalTest.java @@ -19,23 +19,21 @@ */ package org.neo4j.server; -import java.io.IOException; - import org.junit.Test; import org.neo4j.server.helpers.ServerBuilder; -import org.neo4j.server.startup.healthcheck.StartupHealthCheckFailedException; +import org.neo4j.server.preflight.PreflightFailedException; import org.neo4j.test.server.ExclusiveServerTestBase; -public class StartupHealthcheckFunctionalTest extends ExclusiveServerTestBase +public class PreflightTasksFunctionalTest extends ExclusiveServerTestBase { private NeoServer server; - @Test( expected = StartupHealthCheckFailedException.class ) - public void shouldExitWhenFailedStartupHealthCheck() throws IOException + @Test( expected = PreflightFailedException.class ) + public void shouldExitWhenFailedStartupHealthCheck() throws Throwable { server = ServerBuilder.server() - .withFailingStartupHealthcheck() + .withFailingPreflightTasks() .build(); server.start(); } diff --git a/server/src/functionaltest/java/org/neo4j/server/TestStartupTimeout.java b/server/src/functionaltest/java/org/neo4j/server/TestStartupTimeout.java new file mode 100644 index 000000000..b3b1617a7 --- /dev/null +++ b/server/src/functionaltest/java/org/neo4j/server/TestStartupTimeout.java @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.server; + +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Properties; + +import org.junit.After; +import org.junit.Test; +import org.neo4j.kernel.impl.util.FileUtils; +import org.neo4j.kernel.impl.util.StringLogger; +import org.neo4j.server.configuration.Configurator; +import org.neo4j.server.configuration.PropertyFileConfigurator; +import org.neo4j.server.modules.ServerModule; + +public class TestStartupTimeout { + + public static final String HOME_DIRECTORY = "target/" + TestStartupTimeout.class.getSimpleName(); + public static final String STORE_DIRECTORY = HOME_DIRECTORY + "/data/graph.db"; + + public CommunityNeoServer server; + + @After + public void stopServer() + { + if(server != null) + { + server.stop(); + server = null; + } + } + + @Test + public void shouldTimeoutIfStartupTakesLongerThanTimeout() throws IOException + { + Configurator configurator = buildProperties(); + configurator.configuration().setProperty(Configurator.STARTUP_TIMEOUT, 1); + server = createSlowServer(configurator); + + try { + server.start(); + fail("Should have been interrupted."); + } catch(ServerStartupException e) { + // ok! + } + + } + + @Test + public void shouldNotFailIfStartupTakesLessTimeThanTimeout() throws IOException + { + Configurator configurator = buildProperties(); + configurator.configuration().setProperty(Configurator.STARTUP_TIMEOUT, 5); + server = new CommunityNeoServer(configurator){ + @Override + protected Iterable createServerModules(){ + return Arrays.asList(); + } + }; + + try { + server.start(); + Thread.sleep(1000 * 6); + } catch(ServerStartupException e) { + fail("Should not have been interupted."); + } catch (InterruptedException e) { + fail("Should not have been interupted."); + } + } + + @Test + public void shouldNotTimeOutIfTimeoutDisabled() throws IOException + { + Configurator configurator = buildProperties(); + configurator.configuration().setProperty(Configurator.STARTUP_TIMEOUT, 0); + server = createSlowServer(configurator); + + try { + server.start(); + } catch(ServerStartupException e) { + fail("Should not have been interupted."); + } + } + + private CommunityNeoServer createSlowServer(Configurator configurator) { + CommunityNeoServer server = new CommunityNeoServer(configurator){ + @Override + protected Iterable createServerModules(){ + ServerModule slowModule = new ServerModule() { + @Override + public void start(StringLogger logger) { + try { + Thread.sleep(1000 * 5); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public void stop() { } + }; + return Arrays.asList(slowModule); + } + }; + return server; + } + + private Configurator buildProperties() throws IOException + { + FileUtils.deleteRecursively( new File( HOME_DIRECTORY ) ); + new File( HOME_DIRECTORY + "/conf" ).mkdirs(); + + Properties databaseProperties = new Properties(); + String databasePropertiesFileName = HOME_DIRECTORY + "/conf/neo4j.properties"; + databaseProperties.store( new FileWriter( databasePropertiesFileName ), null ); + + Properties serverProperties = new Properties(); + String serverPropertiesFilename = HOME_DIRECTORY + "/conf/neo4j-server.properties"; + serverProperties.setProperty( Configurator.DATABASE_LOCATION_PROPERTY_KEY, STORE_DIRECTORY ); + serverProperties.setProperty( Configurator.DB_TUNING_PROPERTY_FILE_KEY, databasePropertiesFileName ); + serverProperties.setProperty( Configurator.NEO_SERVER_CONFIG_FILE_KEY, serverPropertiesFilename ); + serverProperties.store( new FileWriter(serverPropertiesFilename), null); + + return new PropertyFileConfigurator(new File(serverPropertiesFilename)); + } + +} diff --git a/server/src/functionaltest/java/org/neo4j/server/webadmin/logging/HTTPLoggingFunctionalTest.java b/server/src/functionaltest/java/org/neo4j/server/web/logging/HTTPLoggingFunctionalTest.java similarity index 91% rename from server/src/functionaltest/java/org/neo4j/server/webadmin/logging/HTTPLoggingFunctionalTest.java rename to server/src/functionaltest/java/org/neo4j/server/web/logging/HTTPLoggingFunctionalTest.java index 0865005e1..3a73bb27f 100644 --- a/server/src/functionaltest/java/org/neo4j/server/webadmin/logging/HTTPLoggingFunctionalTest.java +++ b/server/src/functionaltest/java/org/neo4j/server/web/logging/HTTPLoggingFunctionalTest.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.webadmin.logging; +package org.neo4j.server.web.logging; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -33,20 +33,22 @@ import java.util.Scanner; import java.util.UUID; +import org.apache.commons.configuration.Configuration; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.neo4j.server.NeoServer; import org.neo4j.server.configuration.Configurator; +import org.neo4j.server.configuration.MapBasedConfiguration; import org.neo4j.server.helpers.FunctionalTestHelper; import org.neo4j.server.helpers.ServerBuilder; import org.neo4j.server.helpers.ServerHelper; +import org.neo4j.server.preflight.EnsurePreparedForHttpLogging; +import org.neo4j.server.preflight.HTTPLoggingPreparednessRuleTest; +import org.neo4j.server.preflight.PreflightFailedException; import org.neo4j.server.rest.JaxRsResponse; import org.neo4j.server.rest.RestRequest; -import org.neo4j.server.startup.healthcheck.HTTPLoggingPreparednessRule; -import org.neo4j.server.startup.healthcheck.HTTPLoggingPreparednessRuleTest; -import org.neo4j.server.startup.healthcheck.StartupHealthCheckFailedException; import org.neo4j.test.TargetDirectory; import org.neo4j.test.server.ExclusiveServerTestBase; @@ -141,8 +143,12 @@ public void givenConfigurationWithUnwritableLogDirectoryShouldFailToStartServer( final File configFile = HTTPLoggingPreparednessRuleTest.createConfigFile( HTTPLoggingPreparednessRuleTest.createLogbackConfigXml( unwritableLogDir ), confDir ); + Configuration config = new MapBasedConfiguration(); + config.setProperty(Configurator.HTTP_LOGGING, "true"); + config.setProperty(Configurator.HTTP_LOG_CONFIG_LOCATION, configFile.getPath()); + server = ServerBuilder.server().withDefaultDatabaseTuning() - .withStartupHealthCheckRules( new HTTPLoggingPreparednessRule() ) + .withPreflightTasks( new EnsurePreparedForHttpLogging(config) ) .withProperty( Configurator.HTTP_LOGGING, "true" ) .withProperty( Configurator.HTTP_LOG_CONFIG_LOCATION, configFile.getPath() ) .build(); @@ -153,7 +159,7 @@ public void givenConfigurationWithUnwritableLogDirectoryShouldFailToStartServer( server.start(); fail( "should have thrown exception" ); } - catch ( StartupHealthCheckFailedException e ) + catch ( PreflightFailedException e ) { // then assertThat( e.getMessage(), diff --git a/server/src/main/java/org/neo4j/server/AbstractNeoServer.java b/server/src/main/java/org/neo4j/server/AbstractNeoServer.java index b033f20df..564533ef7 100644 --- a/server/src/main/java/org/neo4j/server/AbstractNeoServer.java +++ b/server/src/main/java/org/neo4j/server/AbstractNeoServer.java @@ -47,11 +47,11 @@ import org.neo4j.server.rest.repr.OutputFormatProvider; import org.neo4j.server.rest.repr.RepresentationFormatRepository; import org.neo4j.server.rrd.RrdDbProvider; +import org.neo4j.server.preflight.PreFlightTasks; +import org.neo4j.server.preflight.PreflightFailedException; import org.neo4j.server.security.KeyStoreFactory; import org.neo4j.server.security.KeyStoreInformation; import org.neo4j.server.security.SslCertificateFactory; -import org.neo4j.server.startup.healthcheck.StartupHealthCheck; -import org.neo4j.server.startup.healthcheck.StartupHealthCheckFailedException; import org.neo4j.server.statistic.StatisticCollector; import org.neo4j.server.web.InjectableWrapper; import org.neo4j.server.web.SimpleUriBuilder; @@ -68,16 +68,13 @@ public abstract class AbstractNeoServer implements NeoServer protected WebServer webServer; protected final StatisticCollector statisticsCollector = new StatisticCollector(); - - private StartupHealthCheck startupHealthCheck; - private final List serverModules = new ArrayList(); + private PreFlightTasks preflight; + private final List serverModules = new ArrayList(); private final SimpleUriBuilder uriBuilder = new SimpleUriBuilder(); - - - protected abstract StartupHealthCheck createHealthCheck(); + protected abstract PreFlightTasks createPreflightTasks(); protected abstract Iterable createServerModules(); @@ -89,7 +86,7 @@ public abstract class AbstractNeoServer implements NeoServer @Override public void init() { - this.startupHealthCheck = createHealthCheck(); + this.preflight = createPreflightTasks(); this.database = createDatabase(); this.cypherExecutor = new CypherExecutor( database ); this.webServer = createWebServer(); @@ -101,14 +98,17 @@ public void init() } @Override - public void start() + public void start() throws ServerStartupException { + InterruptThreadTimer interruptStartupTimer = createInterruptStartupTimer(); + try { - // Start at the bottom of the stack and work upwards to the Web - // container - startupHealthCheck(); - + // Pre-flight tasks run outside the boot timeout limit + runPreflightTasks(); + + interruptStartupTimer.startCountdown(); + configureWebServer(); database.start(); @@ -127,8 +127,18 @@ public void start() startWebServer( logger ); logger.logMessage( "--- SERVER STARTUP END ---", true ); - } catch(Throwable t) + + interruptStartupTimer.stopCountdown(); + + }catch(Throwable t) { + if(interruptStartupTimer.wasTriggered()) + { + throw new ServerStartupException( + "Startup took longer than "+interruptStartupTimer.getTimeoutMillis()+"ms, and was stopped. You can disable this behavior by setting '" + Configurator.STARTUP_TIMEOUT + "' to 0.", + 1); + } + if(t instanceof RuntimeException) { throw (RuntimeException)t; @@ -139,7 +149,21 @@ public void start() } } - /** + protected InterruptThreadTimer createInterruptStartupTimer() { + long startupTimeout = getConfiguration().getInt(Configurator.STARTUP_TIMEOUT, Configurator.DEFAULT_STARTUP_TIMEOUT) * 1000; + InterruptThreadTimer stopStartupTimer; + if(startupTimeout > 0) + { + stopStartupTimer = InterruptThreadTimer.createTimer( + startupTimeout, + Thread.currentThread()); + } else { + stopStartupTimer = InterruptThreadTimer.createNoOpTimer(); + } + return stopStartupTimer; + } + + /** * Use this method to register server modules from subclasses * * @param module @@ -173,11 +197,11 @@ private void stopModules() } } - private void startupHealthCheck() + private void runPreflightTasks() { - if ( !startupHealthCheck.run() ) + if ( !preflight.run() ) { - throw new StartupHealthCheckFailedException( startupHealthCheck.failedRule() ); + throw new PreflightFailedException( preflight.failedTask() ); } } diff --git a/server/src/main/java/org/neo4j/server/Bootstrapper.java b/server/src/main/java/org/neo4j/server/Bootstrapper.java index d5c13d045..fcfdecfaf 100644 --- a/server/src/main/java/org/neo4j/server/Bootstrapper.java +++ b/server/src/main/java/org/neo4j/server/Bootstrapper.java @@ -70,11 +70,13 @@ public Integer start() return start( new String[0] ); } + // TODO: This does not use args, check if it is safe to remove them public Integer start( String[] args ) { try { configurator = createConfigurator(); + server = createNeoServer(); server.start(); @@ -100,7 +102,7 @@ public Integer start( String[] args ) } } - protected abstract NeoServer createNeoServer(); + protected abstract NeoServer createNeoServer(); public void stop() { diff --git a/server/src/main/java/org/neo4j/server/CommunityNeoServer.java b/server/src/main/java/org/neo4j/server/CommunityNeoServer.java index a5377e1a5..23cb9e6a8 100644 --- a/server/src/main/java/org/neo4j/server/CommunityNeoServer.java +++ b/server/src/main/java/org/neo4j/server/CommunityNeoServer.java @@ -32,10 +32,10 @@ import org.neo4j.server.modules.StatisticModule; import org.neo4j.server.modules.ThirdPartyJAXRSModule; import org.neo4j.server.modules.WebAdminModule; -import org.neo4j.server.startup.healthcheck.ConfigFileMustBePresentRule; -import org.neo4j.server.startup.healthcheck.HTTPLoggingPreparednessRule; -import org.neo4j.server.startup.healthcheck.Neo4jPropertiesMustExistRule; -import org.neo4j.server.startup.healthcheck.StartupHealthCheck; +import org.neo4j.server.preflight.EnsurePreparedForHttpLogging; +import org.neo4j.server.preflight.PerformRecoveryIfNecessary; +import org.neo4j.server.preflight.PerformUpgradeIfNecessary; +import org.neo4j.server.preflight.PreFlightTasks; import org.neo4j.server.web.Jetty6WebServer; import org.neo4j.server.web.WebServer; @@ -54,11 +54,15 @@ public CommunityNeoServer( Configurator configurator ) } @Override - protected StartupHealthCheck createHealthCheck() { - return new StartupHealthCheck( - new ConfigFileMustBePresentRule(), - new Neo4jPropertiesMustExistRule(), - new HTTPLoggingPreparednessRule() ); + protected PreFlightTasks createPreflightTasks() { + return new PreFlightTasks( + // TODO: Move the config check into bootstrapper + //new EnsureNeo4jPropertiesExist(configurator.configuration()), + new EnsurePreparedForHttpLogging(configurator.configuration()), + new PerformUpgradeIfNecessary(getConfiguration(), + configurator.getDatabaseTuningProperties(), System.out), + new PerformRecoveryIfNecessary(getConfiguration(), + configurator.getDatabaseTuningProperties(), System.out)); } @Override diff --git a/server/src/main/java/org/neo4j/server/InterruptThreadTimer.java b/server/src/main/java/org/neo4j/server/InterruptThreadTimer.java new file mode 100644 index 000000000..148f73d90 --- /dev/null +++ b/server/src/main/java/org/neo4j/server/InterruptThreadTimer.java @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.server; + +import java.util.Timer; +import java.util.TimerTask; + + +/** + * Interrupts a thread after a given timeout, can be cancelled if needed. + */ +public abstract class InterruptThreadTimer +{ + + public static class InterruptThreadTask extends TimerTask + { + private Thread threadToInterrupt; + private boolean wasExecuted = false; + + public InterruptThreadTask(Thread threadToInterrupt) + { + this.threadToInterrupt = threadToInterrupt; + } + + @Override + public void run() { + wasExecuted = true; + threadToInterrupt.interrupt(); + } + + public boolean wasExecuted() + { + return this.wasExecuted; + } + } + + public static InterruptThreadTimer createTimer(long timeoutMillis, Thread threadToInterrupt) + { + return new ActualInterruptThreadTimer(timeoutMillis, threadToInterrupt); + } + + public static InterruptThreadTimer createNoOpTimer() + { + return new NoOpInterruptThreadTimer(); + } + + private static class ActualInterruptThreadTimer extends InterruptThreadTimer + { + private Timer timer = new Timer(); + private final InterruptThreadTask task; + private long timeout; + + public ActualInterruptThreadTimer(long timeoutMillis, Thread threadToInterrupt) + { + this.task = new InterruptThreadTask(threadToInterrupt); + this.timeout = timeoutMillis; + } + + @Override + public void startCountdown() + { + timer.schedule(task, timeout); + } + + @Override + public void stopCountdown() + { + timer.cancel(); + } + + @Override + public boolean wasTriggered() + { + return task.wasExecuted(); + } + + @Override + public long getTimeoutMillis() { + return timeout; + } + } + + private static class NoOpInterruptThreadTimer extends InterruptThreadTimer + { + + public NoOpInterruptThreadTimer() + { + } + + @Override + public void startCountdown() + { + + } + + @Override + public void stopCountdown() + { + + } + + @Override + public boolean wasTriggered() + { + return false; + } + + @Override + public long getTimeoutMillis() { + return 0; + } + } + + public abstract void startCountdown(); + public abstract void stopCountdown(); + public abstract boolean wasTriggered(); + public abstract long getTimeoutMillis(); + +} diff --git a/server/src/main/java/org/neo4j/server/ServerStartupException.java b/server/src/main/java/org/neo4j/server/ServerStartupException.java index ef36b40ed..eb9c72e19 100644 --- a/server/src/main/java/org/neo4j/server/ServerStartupException.java +++ b/server/src/main/java/org/neo4j/server/ServerStartupException.java @@ -23,7 +23,7 @@ * Thrown during start-up of the server, with an explicit error code. */ @SuppressWarnings( "serial" ) -public class ServerStartupException extends Exception +public class ServerStartupException extends RuntimeException { private Integer errorCode; diff --git a/server/src/main/java/org/neo4j/server/WrappingNeoServer.java b/server/src/main/java/org/neo4j/server/WrappingNeoServer.java index 4696fdf72..d4304f442 100644 --- a/server/src/main/java/org/neo4j/server/WrappingNeoServer.java +++ b/server/src/main/java/org/neo4j/server/WrappingNeoServer.java @@ -25,7 +25,7 @@ import org.neo4j.server.configuration.ServerConfigurator; import org.neo4j.server.database.Database; import org.neo4j.server.database.WrappedDatabase; -import org.neo4j.server.startup.healthcheck.StartupHealthCheck; +import org.neo4j.server.preflight.PreFlightTasks; public class WrappingNeoServer extends CommunityNeoServer { @@ -45,8 +45,8 @@ public WrappingNeoServer(GraphDatabaseAPI db, Configurator configurator) } @Override - protected StartupHealthCheck createHealthCheck() { - return new StartupHealthCheck(); + protected PreFlightTasks createPreflightTasks() { + return new PreFlightTasks(); } @Override diff --git a/server/src/main/java/org/neo4j/server/configuration/Configurator.java b/server/src/main/java/org/neo4j/server/configuration/Configurator.java index 1f7bb3dc7..e1e6c37df 100644 --- a/server/src/main/java/org/neo4j/server/configuration/Configurator.java +++ b/server/src/main/java/org/neo4j/server/configuration/Configurator.java @@ -93,8 +93,12 @@ public interface Configurator String DEFAULT_WEBSERVER_HTTPS_KEY_PATH = "neo4j-home/ssl/snakeoil.key"; String HTTP_LOGGING = "org.neo4j.server.http.log.enabled"; + boolean DEFAULT_HTTP_LOGGING = false; String HTTP_LOG_CONFIG_LOCATION = "org.neo4j.server.http.log.config"; String WADL_ENABLED = "unsupported_wadl_generation_enabled"; + + String STARTUP_TIMEOUT = "org.neo4j.server.startup_timeout"; + int DEFAULT_STARTUP_TIMEOUT = 120; Configuration configuration(); diff --git a/server/src/main/java/org/neo4j/server/configuration/ServerConfigurator.java b/server/src/main/java/org/neo4j/server/configuration/ServerConfigurator.java index c7a650567..8e1d9b007 100644 --- a/server/src/main/java/org/neo4j/server/configuration/ServerConfigurator.java +++ b/server/src/main/java/org/neo4j/server/configuration/ServerConfigurator.java @@ -56,6 +56,11 @@ public ServerConfigurator( GraphDatabaseAPI db ) { config.addProperty( DATABASE_LOCATION_PROPERTY_KEY, db.getStoreDir() ); } + + public ServerConfigurator( String dbDir ) + { + config.addProperty( DATABASE_LOCATION_PROPERTY_KEY, dbDir ); + } @Override public Configuration configuration() diff --git a/server/src/main/java/org/neo4j/server/startup/healthcheck/Neo4jPropertiesMustExistRule.java b/server/src/main/java/org/neo4j/server/preflight/EnsureNeo4jPropertiesExist.java similarity index 75% rename from server/src/main/java/org/neo4j/server/startup/healthcheck/Neo4jPropertiesMustExistRule.java rename to server/src/main/java/org/neo4j/server/preflight/EnsureNeo4jPropertiesExist.java index 4419e64b8..c1e1fac26 100644 --- a/server/src/main/java/org/neo4j/server/startup/healthcheck/Neo4jPropertiesMustExistRule.java +++ b/server/src/main/java/org/neo4j/server/preflight/EnsureNeo4jPropertiesExist.java @@ -17,27 +17,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server.preflight; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; +import org.apache.commons.configuration.Configuration; import org.neo4j.server.configuration.Configurator; -public class Neo4jPropertiesMustExistRule implements StartupHealthCheckRule +public class EnsureNeo4jPropertiesExist implements PreflightTask { private static final String EMPTY_STRING = ""; private boolean passed = false; private boolean ran = false; protected String failureMessage = EMPTY_STRING; + private Configuration config; + + public EnsureNeo4jPropertiesExist(Configuration config) + { + this.config = config; + } - public boolean execute( Properties properties ) + @Override + public boolean run() { ran = true; - String configFilename = properties.getProperty( Configurator.NEO_SERVER_CONFIG_FILE_KEY ); + String configFilename = config.getString( Configurator.NEO_SERVER_CONFIG_FILE_KEY ); + if(configFilename == null) + { + failureMessage = String.format( "No server configuration file set, unable to load configuration. Expected system property '%s' to point to config file.", Configurator.NEO_SERVER_CONFIG_FILE_KEY ); + return false; + } + Properties configProperties = new Properties(); FileInputStream inputStream = null; try @@ -74,7 +88,8 @@ protected boolean validateProperties( Properties configProperties ) return true; } - public String getFailureMessage() + @Override + public String getFailureMessage() { if ( passed ) { diff --git a/server/src/main/java/org/neo4j/server/startup/healthcheck/HTTPLoggingPreparednessRule.java b/server/src/main/java/org/neo4j/server/preflight/EnsurePreparedForHttpLogging.java similarity index 85% rename from server/src/main/java/org/neo4j/server/startup/healthcheck/HTTPLoggingPreparednessRule.java rename to server/src/main/java/org/neo4j/server/preflight/EnsurePreparedForHttpLogging.java index 2fc114624..99266c180 100644 --- a/server/src/main/java/org/neo4j/server/startup/healthcheck/HTTPLoggingPreparednessRule.java +++ b/server/src/main/java/org/neo4j/server/preflight/EnsurePreparedForHttpLogging.java @@ -17,37 +17,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server.preflight; import java.io.File; import java.io.IOException; -import java.util.Properties; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import org.apache.commons.configuration.Configuration; import org.apache.commons.io.FileUtils; import org.neo4j.server.configuration.Configurator; import org.w3c.dom.Document; import org.w3c.dom.Node; -public class HTTPLoggingPreparednessRule implements StartupHealthCheckRule +public class EnsurePreparedForHttpLogging implements PreflightTask { private String failureMessage = ""; + private Configuration config; + public EnsurePreparedForHttpLogging(Configuration config) + { + this.config = config; + } + @Override - public boolean execute( Properties properties ) + public boolean run() { - boolean enabled = new Boolean( String.valueOf( properties.getProperty( Configurator.HTTP_LOGGING ) ) ) - .booleanValue(); + boolean enabled = config.getBoolean( Configurator.HTTP_LOGGING, Configurator.DEFAULT_HTTP_LOGGING ); if ( !enabled ) { return true; } - File logLocation = extractLogLocationFromConfig( - String.valueOf( properties.get( Configurator.HTTP_LOG_CONFIG_LOCATION ) ) ); + File logLocation = extractLogLocationFromConfig(config.getString( Configurator.HTTP_LOG_CONFIG_LOCATION ) ); if ( logLocation != null ) diff --git a/server/src/main/java/org/neo4j/server/preflight/PerformRecoveryIfNecessary.java b/server/src/main/java/org/neo4j/server/preflight/PerformRecoveryIfNecessary.java new file mode 100644 index 000000000..d538a11b4 --- /dev/null +++ b/server/src/main/java/org/neo4j/server/preflight/PerformRecoveryIfNecessary.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.server.preflight; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Map; + +import org.apache.commons.configuration.Configuration; +import org.neo4j.kernel.impl.recovery.StoreRecoverer; +import org.neo4j.server.configuration.Configurator; +import org.neo4j.server.logging.Logger; + +public class PerformRecoveryIfNecessary implements PreflightTask { + + private final Logger logger = Logger.getLogger( PerformRecoveryIfNecessary.class ); + private String failureMessage = "Unable to recover database"; + + private Configuration config; + private PrintStream out; + private Map dbConfig; + + public PerformRecoveryIfNecessary(Configuration serverConfig, Map dbConfig, PrintStream out) + { + this.config = serverConfig; + this.dbConfig = dbConfig; + this.out = out; + } + + @Override + public boolean run() { + try { + File dbLocation = new File( config.getString( Configurator.DATABASE_LOCATION_PROPERTY_KEY ) ); + if(dbLocation.exists()) + { + StoreRecoverer recoverer = new StoreRecoverer(); + + if(recoverer.recoveryNeededAt(dbLocation, dbConfig)) + { + out.println("Detected incorrectly shut down database, performing recovery.."); + recoverer.recover(dbLocation, dbConfig); + } + } + + return true; + } catch(IOException e) { + logger.error("Recovery startup task failed.", e); + return false; + } + } + + @Override + public String getFailureMessage() { + return failureMessage; + } + +} diff --git a/server/src/main/java/org/neo4j/server/storemigration/PreStartupStoreUpgrader.java b/server/src/main/java/org/neo4j/server/preflight/PerformUpgradeIfNecessary.java similarity index 62% rename from server/src/main/java/org/neo4j/server/storemigration/PreStartupStoreUpgrader.java rename to server/src/main/java/org/neo4j/server/preflight/PerformUpgradeIfNecessary.java index 3cdea199c..3aece492c 100644 --- a/server/src/main/java/org/neo4j/server/storemigration/PreStartupStoreUpgrader.java +++ b/server/src/main/java/org/neo4j/server/preflight/PerformUpgradeIfNecessary.java @@ -17,15 +17,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -package org.neo4j.server.storemigration; +package org.neo4j.server.preflight; import java.io.File; -import java.io.IOException; import java.io.PrintStream; -import java.util.HashMap; -import java.util.Properties; +import java.util.Map; +import org.apache.commons.configuration.Configuration; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.kernel.DefaultFileSystemAbstraction; import org.neo4j.kernel.DefaultIdGeneratorFactory; @@ -43,63 +41,48 @@ import org.neo4j.kernel.impl.storemigration.monitoring.VisibleMigrationProgressMonitor; import org.neo4j.kernel.impl.util.StringLogger; import org.neo4j.server.configuration.Configurator; -import org.neo4j.server.configuration.PropertyFileConfigurator; -import org.neo4j.server.configuration.validation.DatabaseLocationMustBeSpecifiedRule; -import org.neo4j.server.configuration.validation.Validator; import org.neo4j.server.logging.Logger; -public class PreStartupStoreUpgrader -{ - public static void main( String[] args ) throws IOException - { - PreStartupStoreUpgrader preStartupStoreUpgrader = - new PreStartupStoreUpgrader( System.getProperties(), System.out ); - int exit = preStartupStoreUpgrader.run(); - if ( exit != 0 ) - { - System.exit( exit ); - } - } +public class PerformUpgradeIfNecessary implements PreflightTask { - private final Logger logger = Logger.getLogger( PreStartupStoreUpgrader.class ); - private Properties systemProperties; - private PrintStream out; + private final Logger logger = Logger.getLogger( PerformUpgradeIfNecessary.class ); + private String failureMessage = "Unable to upgrade database"; + private Configuration config; + private PrintStream out; + private Map dbConfig; - public PreStartupStoreUpgrader( Properties systemProperties, PrintStream out ) - { - this.systemProperties = systemProperties; - this.out = out; - } - - public int run() - { - try + public PerformUpgradeIfNecessary(Configuration serverConfig, Map dbConfig, PrintStream out) + { + this.config = serverConfig; + this.dbConfig = dbConfig; + this.out = out; + } + + @Override + public boolean run() { + try { - Configurator configurator = getConfigurator(); - HashMap config = new HashMap( configurator.getDatabaseTuningProperties() ); - - String dbLocation = new File( configurator.configuration() - .getString( Configurator.DATABASE_LOCATION_PROPERTY_KEY ) ).getAbsolutePath(); + String dbLocation = new File( config.getString( Configurator.DATABASE_LOCATION_PROPERTY_KEY ) ).getAbsolutePath(); if ( new CurrentDatabase().storeFilesAtCurrentVersion( new File( dbLocation ) ) ) { - return 0; + return true; } String separator = System.getProperty( "file.separator" ); String store = dbLocation + separator + NeoStore.DEFAULT_NAME; - config.put( "store_dir", dbLocation ); - config.put( "neo_store", store ); + dbConfig.put( "store_dir", dbLocation ); + dbConfig.put( "neo_store", store ); if ( !new UpgradableDatabase().storeFilesUpgradeable( new File( store ) ) ) { logger.info( "Store files missing, or not in suitable state for upgrade. " + "Leaving this problem for main server process to resolve." ); - return 0; + return true; } FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction(); - Config conf = new Config(new ConfigurationDefaults(GraphDatabaseSettings.class ).apply(config) ); + Config conf = new Config(new ConfigurationDefaults(GraphDatabaseSettings.class ).apply(dbConfig) ); StoreUpgrader storeUpgrader = new StoreUpgrader( conf, StringLogger.SYSTEM,new ConfigMapUpgradeConfiguration( conf ), new UpgradableDatabase(), new StoreMigrator( new VisibleMigrationProgressMonitor( out ) ), new DatabaseFiles(), new DefaultIdGeneratorFactory(), fileSystem ); @@ -111,28 +94,27 @@ public int run() catch ( UpgradeNotAllowedByConfigurationException e ) { logger.info( e.getMessage() ); - out.println( e.getMessage() ); - return 1; + out.println(e.getMessage()); + failureMessage = e.getMessage(); + return false; } catch ( StoreUpgrader.UnableToUpgradeException e ) { logger.error( e ); - return 1; + return false; } - return 0; + return true; } catch ( Exception e ) { logger.error( e ); - return 1; + return false; } - } + } - protected Configurator getConfigurator() - { - File configFile = new File( systemProperties.getProperty( Configurator.NEO_SERVER_CONFIG_FILE_KEY, - Configurator.DEFAULT_CONFIG_DIR ) ); - return new PropertyFileConfigurator( new Validator( new DatabaseLocationMustBeSpecifiedRule() ), configFile ); - } + @Override + public String getFailureMessage() { + return failureMessage; + } } diff --git a/server/src/main/java/org/neo4j/server/preflight/PreFlightTasks.java b/server/src/main/java/org/neo4j/server/preflight/PreFlightTasks.java new file mode 100644 index 000000000..616d34fc3 --- /dev/null +++ b/server/src/main/java/org/neo4j/server/preflight/PreFlightTasks.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.server.preflight; + +import org.neo4j.server.logging.Logger; + +/** + * These are tasks that are run on server startup that may take a long time + * to execute, such as recovery, upgrades and so on. + * + * This implementation still needs some work, because of some of the refactoring + * done regarding the NeoServer. Specifically, some of these tasks verify that + * properties files exist and are valid. Other preflight tasks we might want to + * add could be auto-generating config files if they don't exist and creating required + * directories. + * + * All of these (including auto-generating neo4j.properties and creating directories) + * except validating and potentially generating neo4j-server.properties depend on having + * the server configuration available. Eg. we can't both ensure that file exists within these + * tests, while at the same time depending on that file existing. + * + * The validation is not a problem, because we will refactor the server config to use the + * new configuration system from the kernel, which automatically validates itself. + * + * Ensuring the config file exists (and potentially auto-generating it) is a problem. + * Either this need to be split into tasks that have dependencies, and tasks that don't. + * + * Although, it seems it is only this one edge case, so perhaps accepting that and adding + * code to the bootstrapper to ensure the config file exists is acceptable. + */ +public class PreFlightTasks +{ + public static final Logger log = Logger.getLogger( PreFlightTasks.class ); + + private final PreflightTask[] tasks; + + private PreflightTask failedTask = null; + + public PreFlightTasks(PreflightTask... tasks) + { + this.tasks = tasks; + } + + public boolean run() + { + if ( tasks == null || tasks.length < 1 ) + { + return true; + } + + for ( PreflightTask r : tasks ) + { + if ( !r.run() ) + { + log.error( r.getFailureMessage() ); + failedTask = r; + return false; + } + } + + return true; + } + + public PreflightTask failedTask() + { + return failedTask; + } +} diff --git a/server/src/test/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBeSpecifiedAsASystemPropertyRuleTest.java b/server/src/main/java/org/neo4j/server/preflight/PreflightFailedException.java similarity index 71% rename from server/src/test/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBeSpecifiedAsASystemPropertyRuleTest.java rename to server/src/main/java/org/neo4j/server/preflight/PreflightFailedException.java index 5eeff8977..17678fd50 100644 --- a/server/src/test/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBeSpecifiedAsASystemPropertyRuleTest.java +++ b/server/src/main/java/org/neo4j/server/preflight/PreflightFailedException.java @@ -17,16 +17,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server.preflight; -import org.junit.Test; - -public class ConfigFileMustBeSpecifiedAsASystemPropertyRuleTest +@SuppressWarnings( "serial" ) +public class PreflightFailedException extends RuntimeException { - @Test - public void shouldFailWhenSystemPropertyNotSet() + public PreflightFailedException( PreflightTask task ) { - + super( String.format( "Startup failed due to preflight task [%s]: %s", task.getClass(), + task.getFailureMessage() ) ); } - } diff --git a/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckRule.java b/server/src/main/java/org/neo4j/server/preflight/PreflightTask.java similarity index 83% rename from server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckRule.java rename to server/src/main/java/org/neo4j/server/preflight/PreflightTask.java index c8c355a56..194f9d325 100644 --- a/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckRule.java +++ b/server/src/main/java/org/neo4j/server/preflight/PreflightTask.java @@ -17,13 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server.preflight; -import java.util.Properties; -public interface StartupHealthCheckRule +public interface PreflightTask { - public boolean execute( Properties properties ); + public boolean run(); public String getFailureMessage(); } diff --git a/server/src/main/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBePresentRule.java b/server/src/main/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBePresentRule.java deleted file mode 100644 index aecaa6825..000000000 --- a/server/src/main/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBePresentRule.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2002-2012 "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 . - */ -package org.neo4j.server.startup.healthcheck; - -import java.io.File; -import java.util.Properties; - -import org.neo4j.server.configuration.Configurator; - -public class ConfigFileMustBePresentRule implements StartupHealthCheckRule -{ - private static final String EMPTY_STRING = ""; - private boolean passed = false; - private boolean ran = false; - private String failureMessage = EMPTY_STRING; - - public boolean execute( Properties properties ) - { - ran = true; - - String configFilename = properties.getProperty( Configurator.NEO_SERVER_CONFIG_FILE_KEY ); - - if ( configFilename == null ) - { - failureMessage = String.format( "Property [%s] has not been set.", Configurator.NEO_SERVER_CONFIG_FILE_KEY ); - - return false; - } - - File configFile = new File( configFilename ); - if ( !configFile.exists() ) - { - failureMessage = String.format( "No configuration file at [%s]", configFile.getAbsoluteFile() ); - return false; - } - - passed = true; - return passed; - } - - public String getFailureMessage() - { - if ( passed ) - { - return EMPTY_STRING; - } - - if ( !ran ) - { - return String.format( "%s has not been run", getClass().getName() ); - } - else - { - return failureMessage; - } - } -} diff --git a/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheck.java b/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheck.java deleted file mode 100644 index 09827095d..000000000 --- a/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheck.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2002-2012 "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 . - */ -package org.neo4j.server.startup.healthcheck; - -import java.util.Properties; - -import org.neo4j.server.logging.Logger; - -public class StartupHealthCheck -{ - public static final Logger log = Logger.getLogger( StartupHealthCheck.class ); - - private final StartupHealthCheckRule[] rules; - - private StartupHealthCheckRule failedRule = null; - private final Properties properties; - - public StartupHealthCheck( Properties properties , StartupHealthCheckRule... rules) - { - this.rules = rules; - this.properties = properties; - } - - public StartupHealthCheck( StartupHealthCheckRule... rules ) - { - this(System.getProperties(), rules); - } - - public boolean run() - { - if ( rules == null || rules.length < 1 ) - { - return true; - } - - for ( StartupHealthCheckRule r : rules ) - { - if ( !r.execute( properties ) ) - { - log.error( r.getFailureMessage() ); - failedRule = r; - return false; - } - } - - return true; - } - - public StartupHealthCheckRule failedRule() - { - return failedRule; - } -} diff --git a/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckFailedException.java b/server/src/test/java/org/neo4j/server/TestInterruptThreadTimer.java similarity index 50% rename from server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckFailedException.java rename to server/src/test/java/org/neo4j/server/TestInterruptThreadTimer.java index 64fd24425..fc1a2d3f5 100644 --- a/server/src/main/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckFailedException.java +++ b/server/src/test/java/org/neo4j/server/TestInterruptThreadTimer.java @@ -17,14 +17,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server; -@SuppressWarnings( "serial" ) -public class StartupHealthCheckFailedException extends RuntimeException -{ - public StartupHealthCheckFailedException( StartupHealthCheckRule failedRule ) - { - super( String.format( "Startup health check failed due to rule [%s]. %s", failedRule.getClass(), - failedRule.getFailureMessage() ) ); - } +import static org.junit.Assert.fail; + +import org.junit.Test; + + +public class TestInterruptThreadTimer { + + @Test + public void shouldInterruptIfTimeoutIsReached() + { + try { + InterruptThreadTimer timer = InterruptThreadTimer.createTimer(100, Thread.currentThread()); + timer.startCountdown(); + Thread.sleep(3000); + fail("Should have been interrupted."); + } catch(InterruptedException e) + { + // ok + } + } + + @Test + public void shouldNotInterruptIfTimeoutIsNotReached() + { + try { + InterruptThreadTimer timer = InterruptThreadTimer.createTimer(1000 * 10, Thread.currentThread()); + timer.startCountdown(); + Thread.sleep(1); + timer.stopCountdown(); + } catch(InterruptedException e) + { + fail("Should not have been interrupted."); + } + } + } diff --git a/server/src/test/java/org/neo4j/server/helpers/ServerBuilder.java b/server/src/test/java/org/neo4j/server/helpers/ServerBuilder.java index 474d14fc1..cece36745 100644 --- a/server/src/test/java/org/neo4j/server/helpers/ServerBuilder.java +++ b/server/src/test/java/org/neo4j/server/helpers/ServerBuilder.java @@ -45,11 +45,11 @@ import org.neo4j.server.database.CommunityDatabase; import org.neo4j.server.database.Database; import org.neo4j.server.database.EphemeralDatabase; +import org.neo4j.server.preflight.PreFlightTasks; +import org.neo4j.server.preflight.PreflightTask; import org.neo4j.server.rest.paging.Clock; import org.neo4j.server.rest.paging.FakeClock; import org.neo4j.server.rest.paging.LeaseManagerProvider; -import org.neo4j.server.startup.healthcheck.StartupHealthCheck; -import org.neo4j.server.startup.healthcheck.StartupHealthCheckRule; public class ServerBuilder { @@ -58,7 +58,7 @@ public class ServerBuilder protected String dbDir = null; private String webAdminUri = "/db/manage/"; private String webAdminDataUri = "/db/data/"; - protected StartupHealthCheck startupHealthCheck; + protected PreFlightTasks preflightTasks; private final HashMap thirdPartyPackages = new HashMap(); private final Properties arbitraryProperties = new Properties(); @@ -91,9 +91,9 @@ public CommunityNeoServer build() throws IOException } File configFile = createPropertiesFiles(); - if ( startupHealthCheck == null ) + if ( preflightTasks == null ) { - startupHealthCheck = new StartupHealthCheck() + preflightTasks = new PreFlightTasks() { @Override public boolean run() @@ -111,8 +111,8 @@ public boolean run() return new CommunityNeoServer(new PropertyFileConfigurator( new Validator( new DatabaseLocationMustBeSpecifiedRule() ), configFile )) { @Override - protected StartupHealthCheck createHealthCheck() { - return startupHealthCheck; + protected PreFlightTasks createPreflightTasks() { + return preflightTasks; } @Override @@ -319,9 +319,9 @@ public ServerBuilder withoutWebServerPort() return this; } - public ServerBuilder withFailingStartupHealthcheck() + public ServerBuilder withFailingPreflightTasks() { - startupHealthCheck = new StartupHealthCheck() + preflightTasks = new PreFlightTasks() { @Override public boolean run() @@ -330,9 +330,9 @@ public boolean run() } @Override - public StartupHealthCheckRule failedRule() + public PreflightTask failedTask() { - return new StartupHealthCheckRule() + return new PreflightTask() { @Override @@ -342,7 +342,7 @@ public String getFailureMessage() } @Override - public boolean execute( Properties properties ) + public boolean run() { return false; } @@ -418,9 +418,9 @@ public ServerBuilder withProperty( String key, String value ) return this; } - public ServerBuilder withStartupHealthCheckRules( StartupHealthCheckRule... rules ) + public ServerBuilder withPreflightTasks( PreflightTask... tasks ) { - this.startupHealthCheck = new StartupHealthCheck( arbitraryProperties, rules ); + this.preflightTasks = new PreFlightTasks( tasks ); return this; } } diff --git a/server/src/test/java/org/neo4j/server/startup/healthcheck/HTTPLoggingPreparednessRuleTest.java b/server/src/test/java/org/neo4j/server/preflight/HTTPLoggingPreparednessRuleTest.java similarity index 78% rename from server/src/test/java/org/neo4j/server/startup/healthcheck/HTTPLoggingPreparednessRuleTest.java rename to server/src/test/java/org/neo4j/server/preflight/HTTPLoggingPreparednessRuleTest.java index a5647a5f4..d4dc1ab0a 100644 --- a/server/src/test/java/org/neo4j/server/startup/healthcheck/HTTPLoggingPreparednessRuleTest.java +++ b/server/src/test/java/org/neo4j/server/preflight/HTTPLoggingPreparednessRuleTest.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server.preflight; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -28,12 +28,13 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.util.Properties; import java.util.UUID; +import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; import org.junit.Test; import org.neo4j.server.configuration.Configurator; +import org.neo4j.server.configuration.MapBasedConfiguration; import org.neo4j.test.TargetDirectory; public class HTTPLoggingPreparednessRuleTest @@ -42,12 +43,12 @@ public class HTTPLoggingPreparednessRuleTest public void shouldPassWhenExplicitlyDisabled() { // given - HTTPLoggingPreparednessRule rule = new HTTPLoggingPreparednessRule(); - final Properties properties = new Properties(); - properties.put( Configurator.HTTP_LOGGING, "false" ); + Configuration config = new MapBasedConfiguration(); + config.setProperty( Configurator.HTTP_LOGGING, "false" ); + EnsurePreparedForHttpLogging rule = new EnsurePreparedForHttpLogging(config); // when - boolean result = rule.execute( properties ); + boolean result = rule.run( ); // then assertTrue( result ); @@ -58,11 +59,11 @@ public void shouldPassWhenExplicitlyDisabled() public void shouldPassWhenImplicitlyDisabled() { // given - HTTPLoggingPreparednessRule rule = new HTTPLoggingPreparednessRule(); - final Properties properties = new Properties(); + Configuration config = new MapBasedConfiguration(); + EnsurePreparedForHttpLogging rule = new EnsurePreparedForHttpLogging(config); // when - boolean result = rule.execute( properties ); + boolean result = rule.run( ); // then assertTrue( result ); @@ -76,15 +77,14 @@ public void shouldPassWhenEnabledWithGoodConfigSpecified() throws Exception final File logDir = TargetDirectory.forTest( this.getClass() ).directory( "logDir" ); final File confDir = TargetDirectory.forTest( this.getClass() ).directory( "confDir" ); - - HTTPLoggingPreparednessRule rule = new HTTPLoggingPreparednessRule(); - final Properties properties = new Properties(); - properties.put( Configurator.HTTP_LOGGING, "true" ); - properties.put( Configurator.HTTP_LOG_CONFIG_LOCATION, - createConfigFile( createLogbackConfigXml( logDir ), confDir ).getAbsolutePath() ); + Configuration config = new MapBasedConfiguration(); + config.setProperty( Configurator.HTTP_LOGGING, "true" ); + config.setProperty( Configurator.HTTP_LOG_CONFIG_LOCATION, + createConfigFile( createLogbackConfigXml( logDir ), confDir ).getAbsolutePath() ); + EnsurePreparedForHttpLogging rule = new EnsurePreparedForHttpLogging(config); // when - boolean result = rule.execute( properties ); + boolean result = rule.run( ); // then assertTrue( result ); @@ -97,16 +97,15 @@ public void shouldFailWhenEnabledWithUnwritableLogDirSpecifiedInConfig() throws // given final File confDir = TargetDirectory.forTest( this.getClass() ).directory( "confDir" ); - - HTTPLoggingPreparednessRule rule = new HTTPLoggingPreparednessRule(); - final Properties properties = new Properties(); - properties.put( Configurator.HTTP_LOGGING, "true" ); + Configuration config = new MapBasedConfiguration(); + config.setProperty( Configurator.HTTP_LOGGING, "true" ); final File unwritableDirectory = createUnwritableDirectory(); - properties.put( Configurator.HTTP_LOG_CONFIG_LOCATION, + config.setProperty( Configurator.HTTP_LOG_CONFIG_LOCATION, createConfigFile( createLogbackConfigXml( unwritableDirectory ), confDir ).getAbsolutePath() ); + EnsurePreparedForHttpLogging rule = new EnsurePreparedForHttpLogging(config); // when - boolean result = rule.execute( properties ); + boolean result = rule.run( ); // then assertFalse( result ); diff --git a/server/src/test/java/org/neo4j/server/preflight/TestPerformRecoveryIfNecessary.java b/server/src/test/java/org/neo4j/server/preflight/TestPerformRecoveryIfNecessary.java new file mode 100644 index 000000000..e92dfaa91 --- /dev/null +++ b/server/src/test/java/org/neo4j/server/preflight/TestPerformRecoveryIfNecessary.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2002-2012 "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 . + */ +package org.neo4j.server.preflight; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Properties; + +import org.apache.commons.configuration.Configuration; +import org.junit.Test; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.kernel.impl.recovery.StoreRecoverer; +import org.neo4j.kernel.impl.util.FileUtils; +import org.neo4j.server.configuration.Configurator; +import org.neo4j.server.configuration.MapBasedConfiguration; + +public class TestPerformRecoveryIfNecessary { + + public static final String HOME_DIRECTORY = "target/" + TestPerformRecoveryIfNecessary.class.getSimpleName(); + public static final String STORE_DIRECTORY = HOME_DIRECTORY + "/data/graph.db"; + + @Test + public void shouldNotDoAnythingIfNoDBPresent() throws Exception + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Configuration config = buildProperties(); + PerformRecoveryIfNecessary task = new PerformRecoveryIfNecessary(config, new HashMap(), new PrintStream(outputStream)); + + assertThat("Recovery task runs successfully.", task.run(), is(true)); + assertThat("No database should have been created.", new File(STORE_DIRECTORY).exists(), is(false)); + assertThat("Recovery task should not print anything.", outputStream.toString(), is("")); + } + + @Test + public void doesNotPrintAnythingIfDatabaseWasCorrectlyShutdown() throws Exception + { + // Given + Configuration config = buildProperties(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + new GraphDatabaseFactory().newEmbeddedDatabase(STORE_DIRECTORY).shutdown(); + + PerformRecoveryIfNecessary task = new PerformRecoveryIfNecessary(config, new HashMap(), new PrintStream(outputStream)); + + assertThat("Recovery task should run successfully.", task.run(), is(true)); + assertThat("Database should exist.", new File(STORE_DIRECTORY).exists(), is(true)); + assertThat("Recovery should not print anything.", outputStream.toString(), is("")); + } + + @Test + public void shouldPerformRecoveryIfNecessary() throws Exception + { + // Given + StoreRecoverer recoverer = new StoreRecoverer(); + Configuration config = buildProperties(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + new GraphDatabaseFactory().newEmbeddedDatabase(STORE_DIRECTORY).shutdown(); + // Make this look incorrectly shut down + new File(STORE_DIRECTORY, "nioneo_logical.log.active").delete(); + + assertThat("Store should not be recovered", recoverer.recoveryNeededAt(new File(STORE_DIRECTORY), new HashMap()), + is(true)); + + // Run recovery + PerformRecoveryIfNecessary task = new PerformRecoveryIfNecessary(config, new HashMap(), new PrintStream(outputStream)); + assertThat("Recovery task should run successfully.", task.run(), is(true)); + assertThat("Database should exist.", new File(STORE_DIRECTORY).exists(), is(true)); + assertThat("Recovery should print status message.", outputStream.toString(), is("Detected incorrectly shut down database, performing recovery..\n")); + assertThat("Store should be recovered", recoverer.recoveryNeededAt(new File(STORE_DIRECTORY), new HashMap()), + is(false)); + } + + private Configuration buildProperties() throws IOException + { + FileUtils.deleteRecursively( new File( HOME_DIRECTORY ) ); + new File( HOME_DIRECTORY + "/conf" ).mkdirs(); + + Properties databaseProperties = new Properties(); + + String databasePropertiesFileName = HOME_DIRECTORY + "/conf/neo4j.properties"; + databaseProperties.store( new FileWriter( databasePropertiesFileName ), null ); + + Configuration serverProperties = new MapBasedConfiguration(); + serverProperties.setProperty( Configurator.DATABASE_LOCATION_PROPERTY_KEY, STORE_DIRECTORY ); + serverProperties.setProperty( Configurator.DB_TUNING_PROPERTY_FILE_KEY, databasePropertiesFileName ); + + return serverProperties; + } + +} diff --git a/server/src/test/java/org/neo4j/server/storemigration/PreStartupStoreUpgraderTest.java b/server/src/test/java/org/neo4j/server/preflight/TestPerformUpgradeIfNecessary.java similarity index 64% rename from server/src/test/java/org/neo4j/server/storemigration/PreStartupStoreUpgraderTest.java rename to server/src/test/java/org/neo4j/server/preflight/TestPerformUpgradeIfNecessary.java index f82fca465..65d23d551 100644 --- a/server/src/test/java/org/neo4j/server/storemigration/PreStartupStoreUpgraderTest.java +++ b/server/src/test/java/org/neo4j/server/preflight/TestPerformUpgradeIfNecessary.java @@ -17,9 +17,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.storemigration; +package org.neo4j.server.preflight; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.neo4j.kernel.impl.util.FileUtils.copyRecursively; import static org.neo4j.kernel.impl.util.FileUtils.deleteRecursively; @@ -30,32 +32,36 @@ import java.io.IOException; import java.io.PrintStream; import java.net.URL; +import java.util.Map; import java.util.Properties; +import org.apache.commons.configuration.Configuration; import org.junit.Test; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.collection.MapUtil; import org.neo4j.kernel.impl.util.FileUtils; import org.neo4j.server.configuration.Configurator; +import org.neo4j.server.configuration.MapBasedConfiguration; -public class PreStartupStoreUpgraderTest +public class TestPerformUpgradeIfNecessary { - public static final String HOME_DIRECTORY = "target/" + PreStartupStoreUpgraderTest.class.getSimpleName(); + public static final String HOME_DIRECTORY = "target/" + TestPerformUpgradeIfNecessary.class.getSimpleName(); public static final String STORE_DIRECTORY = HOME_DIRECTORY + "/data/graph.db"; @Test public void shouldExitImmediatelyIfStoreIsAlreadyAtLatestVersion() throws IOException { - Properties systemProperties = buildProperties( false ); + Configuration serverConfig = buildProperties( false ); new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( STORE_DIRECTORY ).newGraphDatabase().shutdown(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PreStartupStoreUpgrader upgrader = new PreStartupStoreUpgrader( systemProperties, - new PrintStream( outputStream ) ); + PerformUpgradeIfNecessary upgrader = new PerformUpgradeIfNecessary( serverConfig, + loadNeo4jProperties(), new PrintStream( outputStream ) ); - int exit = upgrader.run(); + boolean exit = upgrader.run(); - assertEquals( 0, exit ); + assertEquals( true, exit ); assertEquals( "", new String( outputStream.toByteArray() ) ); } @@ -63,20 +69,21 @@ public void shouldExitImmediatelyIfStoreIsAlreadyAtLatestVersion() throws IOExce @Test public void shouldGiveHelpfulMessageIfAutoUpgradeParameterNotSet() throws IOException { - Properties systemProperties = buildProperties( false ); + Configuration serverProperties = buildProperties( false ); prepareSampleLegacyDatabase( new File( STORE_DIRECTORY ) ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PreStartupStoreUpgrader upgrader = new PreStartupStoreUpgrader( systemProperties, - new PrintStream( outputStream ) ); + PerformUpgradeIfNecessary upgrader = new PerformUpgradeIfNecessary( serverProperties, + loadNeo4jProperties(), new PrintStream( outputStream ) ); - int exit = upgrader.run(); - - assertEquals( 1, exit ); + boolean exit = upgrader.run(); + assertEquals( false, exit ); + String[] lines = new String( outputStream.toByteArray() ).split( "\\r?\\n" ); - assertTrue( lines[0].contains( "To enable automatic upgrade, please set configuration parameter " + - "\"allow_store_upgrade=true\"" ) ); + assertThat( "'" + lines[0] + "' contains '" + "To enable automatic upgrade, please set configuration parameter " + + "\"allow_store_upgrade=true\"", lines[0].contains("To enable automatic upgrade, please set configuration parameter " + + "\"allow_store_upgrade=true\""), is(true) ); } @Test @@ -84,12 +91,12 @@ public void shouldExitCleanlyIfDatabaseMissingSoThatDatabaseCreationIsLeftToMain { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PreStartupStoreUpgrader upgrader = new PreStartupStoreUpgrader( buildProperties( true ), - new PrintStream( outputStream ) ); + PerformUpgradeIfNecessary upgrader = new PerformUpgradeIfNecessary( buildProperties( true ), + loadNeo4jProperties(), new PrintStream( outputStream ) ); - int exit = upgrader.run(); + boolean exit = upgrader.run(); - assertEquals( 0, exit ); + assertEquals( true, exit ); assertEquals( "", new String( outputStream.toByteArray() ) ); } @@ -97,16 +104,16 @@ public void shouldExitCleanlyIfDatabaseMissingSoThatDatabaseCreationIsLeftToMain @Test public void shouldUpgradeDatabase() throws IOException { - Properties systemProperties = buildProperties( true ); + Configuration serverConfig = buildProperties( true ); prepareSampleLegacyDatabase( new File( STORE_DIRECTORY ) ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PreStartupStoreUpgrader upgrader = new PreStartupStoreUpgrader( systemProperties, - new PrintStream( outputStream ) ); + PerformUpgradeIfNecessary upgrader = new PerformUpgradeIfNecessary( serverConfig, + loadNeo4jProperties(), new PrintStream( outputStream ) ); - int exit = upgrader.run(); + boolean exit = upgrader.run(); - assertEquals( 0, exit ); + assertEquals( true, exit ); String[] lines = new String( outputStream.toByteArray() ).split( "\\r?\\n" ); assertEquals( "Starting upgrade of database store files", lines[0] ); @@ -114,7 +121,7 @@ public void shouldUpgradeDatabase() throws IOException assertEquals( "Finished upgrade of database store files", lines[2] ); } - private Properties buildProperties(boolean allowStoreUpgrade) throws IOException + private Configuration buildProperties(boolean allowStoreUpgrade) throws IOException { FileUtils.deleteRecursively( new File( HOME_DIRECTORY ) ); new File( HOME_DIRECTORY + "/conf" ).mkdirs(); @@ -127,15 +134,17 @@ private Properties buildProperties(boolean allowStoreUpgrade) throws IOException String databasePropertiesFileName = HOME_DIRECTORY + "/conf/neo4j.properties"; databaseProperties.store( new FileWriter( databasePropertiesFileName ), null ); - Properties serverProperties = new Properties(); + Configuration serverProperties = new MapBasedConfiguration(); serverProperties.setProperty( Configurator.DATABASE_LOCATION_PROPERTY_KEY, STORE_DIRECTORY ); serverProperties.setProperty( Configurator.DB_TUNING_PROPERTY_FILE_KEY, databasePropertiesFileName ); - String serverPropertiesFileName = HOME_DIRECTORY + "/conf/neo4j-server.properties"; - serverProperties.store( new FileWriter( serverPropertiesFileName ), null ); - Properties systemProperties = new Properties(); - systemProperties.put( Configurator.NEO_SERVER_CONFIG_FILE_KEY, serverPropertiesFileName ); - return systemProperties; + return serverProperties; + } + + private Map loadNeo4jProperties() throws IOException + { + String databasePropertiesFileName = HOME_DIRECTORY + "/conf/neo4j.properties"; + return MapUtil.load(new File(databasePropertiesFileName)); } public static void prepareSampleLegacyDatabase( File workingDirectory ) throws IOException @@ -150,7 +159,7 @@ public static void prepareSampleLegacyDatabase( File workingDirectory ) throws I public static File findOldFormatStoreDirectory() { - URL legacyStoreResource = PreStartupStoreUpgraderTest.class.getResource( "legacystore/exampledb/neostore" ); + URL legacyStoreResource = TestPerformUpgradeIfNecessary.class.getResource( "legacystore/exampledb/neostore" ); return new File( legacyStoreResource.getFile() ).getParentFile(); } diff --git a/server/src/test/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckTest.java b/server/src/test/java/org/neo4j/server/preflight/TestPreflightTasks.java similarity index 69% rename from server/src/test/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckTest.java rename to server/src/test/java/org/neo4j/server/preflight/TestPreflightTasks.java index 17d21adad..6c2317eba 100644 --- a/server/src/test/java/org/neo4j/server/startup/healthcheck/StartupHealthCheckTest.java +++ b/server/src/test/java/org/neo4j/server/preflight/TestPreflightTasks.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.server.startup.healthcheck; +package org.neo4j.server.preflight; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertFalse; @@ -25,40 +25,40 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import java.util.Properties; - import org.junit.Test; import org.neo4j.server.logging.InMemoryAppender; +import org.neo4j.server.preflight.PreFlightTasks; +import org.neo4j.server.preflight.PreflightTask; -public class StartupHealthCheckTest +public class TestPreflightTasks { @Test public void shouldPassWithNoRules() { - StartupHealthCheck check = new StartupHealthCheck(); + PreFlightTasks check = new PreFlightTasks(); assertTrue( check.run() ); } @Test public void shouldRunAllHealthChecksToCompletionIfNonFail() { - StartupHealthCheck check = new StartupHealthCheck( getPassingRules() ); + PreFlightTasks check = new PreFlightTasks( getPassingRules() ); assertTrue( check.run() ); } @Test public void shouldFailIfOneOrMoreHealthChecksFail() { - StartupHealthCheck check = new StartupHealthCheck( getWithOneFailingRule() ); + PreFlightTasks check = new PreFlightTasks( getWithOneFailingRule() ); assertFalse( check.run() ); } @Test public void shouldLogFailedRule() { - StartupHealthCheck check = new StartupHealthCheck( getWithOneFailingRule() ); - InMemoryAppender appender = new InMemoryAppender( StartupHealthCheck.log ); + PreFlightTasks check = new PreFlightTasks( getWithOneFailingRule() ); + InMemoryAppender appender = new InMemoryAppender( PreFlightTasks.log ); check.run(); // Previously we tested on "SEVERE: blah blah" but that's a string @@ -70,21 +70,21 @@ public void shouldLogFailedRule() @Test public void shouldAdvertiseFailedRule() { - StartupHealthCheck check = new StartupHealthCheck( getWithOneFailingRule() ); + PreFlightTasks check = new PreFlightTasks( getWithOneFailingRule() ); check.run(); - assertNotNull( check.failedRule() ); + assertNotNull( check.failedTask() ); } - private StartupHealthCheckRule[] getWithOneFailingRule() + private PreflightTask[] getWithOneFailingRule() { - StartupHealthCheckRule[] rules = new StartupHealthCheckRule[5]; + PreflightTask[] rules = new PreflightTask[5]; for ( int i = 0; i < rules.length; i++ ) { - rules[i] = new StartupHealthCheckRule() + rules[i] = new PreflightTask() { @Override - public boolean execute( Properties properties ) + public boolean run() { return true; } @@ -97,10 +97,10 @@ public String getFailureMessage() }; } - rules[rules.length / 2] = new StartupHealthCheckRule() + rules[rules.length / 2] = new PreflightTask() { @Override - public boolean execute( Properties properties ) + public boolean run() { return false; } @@ -115,16 +115,16 @@ public String getFailureMessage() return rules; } - private StartupHealthCheckRule[] getPassingRules() + private PreflightTask[] getPassingRules() { - StartupHealthCheckRule[] rules = new StartupHealthCheckRule[5]; + PreflightTask[] rules = new PreflightTask[5]; for ( int i = 0; i < rules.length; i++ ) { - rules[i] = new StartupHealthCheckRule() + rules[i] = new PreflightTask() { @Override - public boolean execute( Properties properties ) + public boolean run() { return true; } diff --git a/server/src/test/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBePresentRuleTest.java b/server/src/test/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBePresentRuleTest.java deleted file mode 100644 index 017c8aecb..000000000 --- a/server/src/test/java/org/neo4j/server/startup/healthcheck/ConfigFileMustBePresentRuleTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2002-2012 "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 . - */ -package org.neo4j.server.startup.healthcheck; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.util.Properties; - -import org.junit.Test; -import org.neo4j.server.ServerTestUtils; -import org.neo4j.server.configuration.Configurator; - -public class ConfigFileMustBePresentRuleTest -{ - @Test - public void shouldFailIfThereIsNoSystemPropertyForConfigFile() - { - ConfigFileMustBePresentRule rule = new ConfigFileMustBePresentRule(); - assertFalse( rule.execute( propertiesWithoutConfigFileLocation() ) ); - } - - @Test - public void shouldFailIfThereIsNoConfigFileWhereTheSystemPropertyPoints() throws IOException - { - ConfigFileMustBePresentRule rule = new ConfigFileMustBePresentRule(); - File tempFile = ServerTestUtils.createTempPropertyFile(); - tempFile.delete(); - - assertFalse( rule.execute( propertiesWithConfigFileLocation( tempFile ) ) ); - } - - @Test - public void shouldPassIfThereIsAConfigFileWhereTheSystemPropertyPoints() throws IOException - { - File propertyFile = ServerTestUtils.createTempPropertyFile(); - ServerTestUtils.writePropertyToFile( Configurator.DATABASE_LOCATION_PROPERTY_KEY, "/tmp/foo.db", propertyFile ); - - ConfigFileMustBePresentRule rule = new ConfigFileMustBePresentRule(); - assertTrue( rule.execute( propertiesWithConfigFileLocation( propertyFile ) ) ); - propertyFile.delete(); - } - - private Properties propertiesWithoutConfigFileLocation() - { - System.clearProperty( Configurator.NEO_SERVER_CONFIG_FILE_KEY ); - return System.getProperties(); - } - - private Properties propertiesWithConfigFileLocation( File propertyFile ) - { - System.setProperty( Configurator.NEO_SERVER_CONFIG_FILE_KEY, propertyFile.getAbsolutePath() ); - return System.getProperties(); - } -} diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/active_tx_log b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/active_tx_log similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/active_tx_log rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/active_tx_log diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/index/lucene-store.db b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/index/lucene-store.db similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/index/lucene-store.db rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/index/lucene-store.db diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/index/lucene.log.active b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/index/lucene.log.active similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/index/lucene.log.active rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/index/lucene.log.active diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/messages.log b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/messages.log similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/messages.log rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/messages.log diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.nodestore.db b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.nodestore.db similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.nodestore.db rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.nodestore.db diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.nodestore.db.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.nodestore.db.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.nodestore.db.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.nodestore.db.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.arrays b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.arrays similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.arrays rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.arrays diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.arrays.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.arrays.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.arrays.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.arrays.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index.keys b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index.keys similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index.keys rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index.keys diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index.keys.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index.keys.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.index.keys.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.index.keys.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.strings b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.strings similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.strings rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.strings diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.strings.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.strings.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.propertystore.db.strings.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.propertystore.db.strings.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshipstore.db b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshipstore.db similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshipstore.db rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshipstore.db diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshipstore.db.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshipstore.db.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshipstore.db.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshipstore.db.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db.names b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db.names similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db.names rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db.names diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db.names.id b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db.names.id similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/neostore.relationshiptypestore.db.names.id rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/neostore.relationshiptypestore.db.names.id diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/nioneo_logical.log.active b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/nioneo_logical.log.active similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/nioneo_logical.log.active rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/nioneo_logical.log.active diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/tm_tx_log.1 b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/tm_tx_log.1 similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/tm_tx_log.1 rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/tm_tx_log.1 diff --git a/server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/tm_tx_log.2 b/server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/tm_tx_log.2 similarity index 100% rename from server/src/test/resources/org/neo4j/server/storemigration/legacystore/exampledb/tm_tx_log.2 rename to server/src/test/resources/org/neo4j/server/preflight/legacystore/exampledb/tm_tx_log.2