package org.exist.test;

import org.exist.EXistException;
import org.exist.storage.BrokerPool;
import org.exist.util.Configuration;
import org.exist.util.ConfigurationHelper;
import org.exist.util.DatabaseConfigurationException;
import org.exist.util.FileUtils;
import org.junit.rules.ExternalResource;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;

/**
 * Exist embedded Server Rule for JUnit
 */
public class ExistEmbeddedServer extends ExternalResource {

    private final Optional<String> instanceName;
    private final Optional<Path> configFile;
    private final Optional<Properties> configProperties;

    private BrokerPool pool = null;

    public ExistEmbeddedServer() {
        this(null, null, null);
    }

    public ExistEmbeddedServer(final Properties configProperties) {
        this(null, null, configProperties);
    }

    public ExistEmbeddedServer(final String instanceName, final Path configFile) {
        this(instanceName, configFile, null);
    }

    public ExistEmbeddedServer(final String instanceName, final Path configFile, final Properties configProperties) {
        this.instanceName = Optional.ofNullable(instanceName);
        this.configFile = Optional.ofNullable(configFile);
        this.configProperties = Optional.ofNullable(configProperties);
    }

    @Override
    protected void before() throws Throwable {
        startDb();
        super.before();
    }

    private void startDb() throws DatabaseConfigurationException, EXistException {
        if(pool == null) {

            final String name = instanceName.orElse(BrokerPool.DEFAULT_INSTANCE_NAME);

            final Optional<Path> home = Optional.ofNullable(System.getProperty("exist.home", System.getProperty("user.dir"))).map(Paths::get);
            final Path confFile = configFile.orElseGet(() -> ConfigurationHelper.lookup("conf.xml", home));

            final Configuration config;
            if(confFile.isAbsolute() && Files.exists(confFile)) {
                //TODO(AR) is this correct?
                config = new Configuration(confFile.toAbsolutePath().toString());
            } else {
                config = new Configuration(FileUtils.fileName(confFile), home);
            }

            // override any specified config properties
            if(configProperties.isPresent()) {
                for(final Map.Entry<Object, Object> configProperty : configProperties.get().entrySet()) {
                    config.setProperty(configProperty.getKey().toString(), configProperty.getValue());
                }
            }

            BrokerPool.configure(name,1, 5, config, Optional.empty());
            this.pool = BrokerPool.getInstance(name);
        } else {
            throw new IllegalStateException("ExistEmbeddedServer already running");
        }
    }

    public BrokerPool getBrokerPool() {
        return pool;
    }

    public void restart() throws EXistException, DatabaseConfigurationException {
        if(pool != null) {
            stopDb();
            startDb();
        } else {
            throw new IllegalStateException("ExistEmbeddedServer already stopped");
        }
    }

    @Override
    protected void after() {
        stopDb();
        super.after();
    }

    private void stopDb() {
        if(pool != null) {
            pool.shutdown();

            // clear instance variables
            pool = null;
        } else {
            throw new IllegalStateException("ExistEmbeddedServer already stopped");
        }
    }
}

@@ -88,16 +88,58 @@
 erPool;%0A
+import org.exist.storage.journal.Journal;%0A
 import o
@@ -339,16 +339,44 @@
 ource;%0A%0A
+import java.io.IOException;%0A
 import j
@@ -793,110 +793,356 @@
 es;%0A
-%0A    private BrokerPool pool = null;%0A%0A    public ExistEmbeddedServer() %7B%0A        this(null, null, null
+    private final boolean useTemporaryStorage;%0A    private Optional%3CPath%3E temporaryStorage = Optional.empty();%0A%0A    private BrokerPool pool = null;%0A%0A    public ExistEmbeddedServer() %7B%0A        this(null, null, null, false);%0A    %7D%0A%0A    public ExistEmbeddedServer(final boolean useTemporaryStorage) %7B%0A        this(null, null, null, useTemporaryStorage
 );%0A 
@@ -1248,32 +1248,39 @@
 configProperties
+, false
 );%0A    %7D%0A%0A    pu
@@ -1399,16 +1399,23 @@
 le, null
+, false
 );%0A    %7D
@@ -1526,32 +1526,257 @@
 igProperties) %7B%0A
+        this(instanceName, configFile, configProperties, false);%0A    %7D%0A%0A    public ExistEmbeddedServer(final String instanceName, final Path configFile, final Properties configProperties, final boolean useTemporaryStorage) %7B%0A
         this.ins
@@ -1952,16 +1952,72 @@
 rties);%0A
+        this.useTemporaryStorage = useTemporaryStorage;%0A
     %7D%0A%0A 
@@ -2198,32 +2198,45 @@
 , EXistException
+, IOException
  %7B%0A        if(po
@@ -3275,32 +3275,520 @@
 %0A            %7D%0A%0A
+            if(useTemporaryStorage) %7B%0A                this.temporaryStorage = Optional.of(Files.createTempDirectory(%22org.exist.test.ExistEmbeddedServer%22));%0A                config.setProperty(BrokerPool.PROPERTY_DATA_DIR, temporaryStorage.get());%0A                config.setProperty(Journal.RECOVERY_JOURNAL_DIR_ATTRIBUTE, temporaryStorage.get());%0A                System.out.println(%22Using temporary storage location: %22 + temporaryStorage.get().toAbsolutePath().toString());%0A            %7D%0A%0A
             Brok
@@ -4153,32 +4153,45 @@
 urationException
+, IOException
  %7B%0A        if(po
@@ -4607,16 +4607,219 @@
  = null;
+%0A%0A            if(useTemporaryStorage && temporaryStorage.isPresent()) %7B%0A                FileUtils.deleteQuietly(temporaryStorage.get());%0A                temporaryStorage = Optional.empty();%0A            %7D
 %0A       

Allow temporary storage to be used for the database during test