/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product may include a number of subcomponents with
* separate copyright notices and license terms. Your use of the source
* code for these subcomponents is subject to the terms and
* conditions of the subcomponent's license, as noted in the LICENSE file.
*/
package org.neo4j.ogm.testutil;
import java.io.FileWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.io.IOUtils;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.harness.ServerControls;
import org.neo4j.harness.TestServerBuilders;
import org.neo4j.server.AbstractNeoServer;
import org.neo4j.server.database.Database;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Vince Bickers
* @author Mark Angrish
*/
public class TestServer {
private static final Logger LOGGER = LoggerFactory.getLogger(TestServer.class);
private final Integer port;
private final Integer transactionTimeoutSeconds;
private final Boolean enableAuthentication;
private final Boolean enableBolt;
private GraphDatabaseService database;
private ServerControls controls;
private String username;
private String password;
private String uri;
TestServer(boolean enableAuthentication, boolean enableBolt, int transactionTimeoutSeconds) {
this.port = TestUtils.getAvailablePort();
this.transactionTimeoutSeconds = transactionTimeoutSeconds;
this.enableAuthentication = enableAuthentication;
this.enableBolt = enableBolt;
startServer();
LOGGER.info("Starting {} server on: {}", enableBolt ? "BOLT": "HTTP", port);
}
private void startServer() {
try {
if (enableBolt) {
controls = TestServerBuilders.newInProcessBuilder()
.withConfig("dbms.connector.0.enabled", "true")
.withConfig("dbms.connector.0.address", "localhost:" + String.valueOf(port))
.newServer();
} else {
controls = TestServerBuilders.newInProcessBuilder()
.withConfig("dbms.connector.1.enabled", "true")
.withConfig("dbms.connector.1.address", "localhost:" + String.valueOf(port))
.withConfig("dbms.security.auth_enabled", String.valueOf(enableAuthentication))
.withConfig("org.neo4j.server.webserver.port", String.valueOf(port))
.withConfig("org.neo4j.server.transaction.timeout", String.valueOf(transactionTimeoutSeconds))
.withConfig("dbms.transaction_timeout", String.valueOf(transactionTimeoutSeconds))
.withConfig("dbms.security.auth_store.location", createAuthStore())
.withConfig("unsupported.dbms.security.auth_store.location", createAuthStore())
.withConfig("remote_shell_enabled", "false")
.newServer();
}
initialise(controls);
// ensure we shutdown this server when the JVM terminates, if its not been shutdown by user code
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
} catch (Exception e) {
throw new RuntimeException("Error starting in-process server", e);
}
}
private String createAuthStore() {
// creates a temp auth store, with encrypted credentials "neo4j:password" if the server is authenticating connections
try {
Path authStore = Files.createTempFile("neo4j", "credentials");
authStore.toFile().deleteOnExit();
if (enableAuthentication) {
try (Writer authStoreWriter = new FileWriter(authStore.toFile())) {
IOUtils.write("neo4j:SHA-256,03C9C54BF6EEF1FF3DFEB75403401AA0EBA97860CAC187D6452A1FCF4C63353A,819BDB957119F8DFFF65604C92980A91:", authStoreWriter);
}
this.username = "neo4j";
this.password = "password";
}
return authStore.toAbsolutePath().toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void initialise(ServerControls controls) throws Exception {
setDatabase(controls);
this.uri = url();
}
private void setDatabase(ServerControls controls) throws Exception {
try {
Method method = controls.getClass().getMethod("graph");
database = (GraphDatabaseService) method.invoke(controls);
} catch (NoSuchMethodException nsme) {
Class clazz = Class.forName("org.neo4j.harness.internal.InProcessServerControls");
Field field = clazz.getDeclaredField("server");
field.setAccessible(true);
AbstractNeoServer server = (AbstractNeoServer) field.get(controls);
Database db = server.getDatabase();
database = db.getGraph();
}
}
/**
* Stops the underlying server bootstrapper and, in turn, the Neo4j server.
*/
private void shutdown() {
if (database != null && database.isAvailable(100)) {
LOGGER.info("Stopping {} server on: {}", enableBolt ? "BOLT": "HTTP", port);
database.shutdown();
database = null;
}
controls.close();
}
/**
* Waits for a period of time and checks the database availability afterwards
*
* @return true if the database is available, false otherwise
*/
boolean isRunning() {
return database.isAvailable((long) 1000);
}
/**
* Retrieves the base URL of the Neo4j database server used in the test.
*
* @return The URL of the Neo4j test server
*/
private String url() {
Method method;
try {
if (enableBolt) {
method = controls.getClass().getMethod("boltURI");
} else {
method = controls.getClass().getMethod("httpURI");
}
Object url = method.invoke(controls);
return url.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Retrieves the underlying {@link org.neo4j.graphdb.GraphDatabaseService} used in this test.
*
* @return The test {@link org.neo4j.graphdb.GraphDatabaseService}
*/
public GraphDatabaseService getGraphDatabaseService() {
return this.database;
}
public String getUri() {
return uri;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
}