Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ body:
options:
- Core
- ActiveMQ
- ArcadeDB
- Azure
- Cassandra
- ChromaDB
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/enhancement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ body:
options:
- Core
- ActiveMQ
- ArcadeDB
- Azure
- Cassandra
- ChromaDB
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ body:
options:
- Core
- ActiveMQ
- ArcadeDB
- Azure
- Cassandra
- ChromaDB
Expand Down
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ updates:
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/arcadedb"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/azure"
schedule:
Expand Down
4 changes: 4 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
- changed-files:
- any-glob-to-any-file:
- modules/activemq/**/*
"modules/arcadedb":
- changed-files:
- any-glob-to-any-file:
- modules/arcadedb/**/*
"modules/azure":
- changed-files:
- any-glob-to-any-file:
Expand Down
56 changes: 56 additions & 0 deletions docs/modules/databases/arcadedb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# ArcadeDB Module

Testcontainers module for [ArcadeDB](https://hub.docker.com/u/arcadedata)

## Usage example

You can start an ArcadeDB container instance from any Java application by using:

<!--codeinclude-->
[Container creation](../../../modules/arcadedb/src/test/java/org/testcontainers/containers/ArcadeDBContainerTest.java) inside_block:container
<!--/codeinclude-->

## Adding this module to your project dependencies

Add the following dependency to your `pom.xml`/`build.gradle` file:

=== "Gradle"
```groovy
testImplementation "org.testcontainers:arcadedb:25.3.2"
```
=== "Maven"
```xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>orientdb</artifactId>
<version>25.3.2</version>
<scope>test</scope>
</dependency>
```

!!! hint
Add the following dependencies if you plan to access the Testcontainer:

=== "Gradle"
```groovy
compile "com.arcadedb:arcadedb-engine:25.3.2"
compile "com.arcadedb:arcadedb-network:25.3.2"
```

=== "Maven"
```xml
<dependency>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-engine</artifactId>
<version>25.7.1</version>
</dependency>
<dependency>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-network</artifactId>
<version>25.7.1</version>
</dependency>
```




17 changes: 17 additions & 0 deletions modules/arcadedb/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
description = "Testcontainers :: ArcadeDB"

dependencies {
api project(":testcontainers")

api "com.arcadedb:arcadedb-engine:25.3.2"
api "com.arcadedb:arcadedb-network:25.3.2"
testImplementation 'org.assertj:assertj-core:3.27.4'
testImplementation 'org.apache.tinkerpop:gremlin-driver:3.7.3'
testImplementation "com.arcadedb:arcadedb-gremlin:25.3.2"
}

tasks.japicmp {
classExcludes = [
"org.testcontainers.containers.ArcadeDBContainer"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package org.testcontainers.containers;

import com.arcadedb.remote.RemoteDatabase;
import com.arcadedb.remote.RemoteServer;
import com.github.dockerjava.api.command.InspectContainerResponse;
import lombok.Getter;
import lombok.NonNull;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

/**
* Testcontainers implementation for ArcadeDB.
* <p>
* Supported image: {@code arcadedb} with JDK version up to 17
* <p>
* Exposed ports:
* <ul>
* <li>Database: 2424</li>
* <li>Remote database and Studio: 2480</li>
* </ul>
*/
public class ArcadeDBContainer extends GenericContainer<ArcadeDBContainer> {

private static final Logger LOGGER = LoggerFactory.getLogger(ArcadeDBContainer.class);

private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("arcadedata/arcadedb");

private static final String DEFAULT_SERVER_PASSWORD = "playwithdata";

private static final String DEFAULT_DATABASE_NAME = "testcontainers";

private static final int DEFAULT_BINARY_PORT = 2424;

private static final int DEFAULT_HTTP_PORT = 2480;

@Getter
private String databaseName;

private String serverPassword;

private int serverPort;

private Optional<String> scriptPath = Optional.empty();

private RemoteServer remoteServer;

private RemoteDatabase database;

public ArcadeDBContainer(@NonNull String dockerImageName) {
this(DockerImageName.parse(dockerImageName));
}

public ArcadeDBContainer(final DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);

serverPort = DEFAULT_HTTP_PORT;
serverPassword = DEFAULT_SERVER_PASSWORD;
databaseName = DEFAULT_DATABASE_NAME;

waitStrategy = Wait.forLogMessage(".*ArcadeDB Server started.*", 1);

addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT);
}

@Override
protected void configure() {
final String javaOpts = String.format("-Darcadedb.server.rootPassword=%s", serverPassword);

addEnv("JAVA_OPTS", javaOpts);
}

public synchronized RemoteDatabase getDatabase() {
final String host = getHost();
final Integer port = getMappedPort(serverPort);
if (remoteServer == null) {
try {
remoteServer = new RemoteServer(host, port, "root", serverPassword);
} catch (Exception e) {
final String msg = String.format(
"Could not connect to server %s:%d with user 'root' due to %s",
host,
port,
e.getMessage()
);
LOGGER.error(msg, e);
throw new IllegalStateException(msg, e);
}
}

if (!remoteServer.exists(getDatabaseName())) {
remoteServer.create(getDatabaseName());
}

if (database != null && database.isOpen()) {
return database;
}

try {
database = new RemoteDatabase(host, port, getDatabaseName(), "root", serverPassword);
scriptPath.ifPresent(path -> loadScript(path, database));
return database;
} catch (Exception e) {
final String msg = String.format(
"Could not connect to database %s on server %s:%d due to %s",
getDatabaseName(),
host,
port,
e.getMessage()
);
LOGGER.error(msg, e);
throw new IllegalStateException(msg, e);
}
}

public ArcadeDBContainer withDatabaseName(final String databaseName) {
this.databaseName = databaseName;
return self();
}

public ArcadeDBContainer withServerPassword(final String serverPassword) {
this.serverPassword = serverPassword;
return self();
}

public ArcadeDBContainer withServerPort(final int serverPort) {
this.serverPort = serverPort;
return self();
}

public ArcadeDBContainer withScriptPath(String scriptPath) {
this.scriptPath = Optional.of(scriptPath);
return self();
}

@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {
final String host = getHost();
final Integer port = getMappedPort(serverPort);

try {
remoteServer = new RemoteServer(host, port, "root", serverPassword);
} catch (Exception e) {
final String msg = String.format(
"Could not connect to server %s:%d with user 'root' due to %s",
host,
port,
e.getMessage()
);
LOGGER.error(msg, e);
throw new IllegalStateException(msg, e);
}
}

private void loadScript(String path, RemoteDatabase db) {
try {
URL resource = getClass().getClassLoader().getResource(path);

if (resource == null) {
LOGGER.warn("Could not load classpath init script: {}", scriptPath);
throw new RuntimeException(
"Could not load classpath init script: " + scriptPath + ". Resource not found."
);
}

String script = IOUtils.toString(resource, StandardCharsets.UTF_8);

db.command("sqlscript", script);
} catch (IOException e) {
LOGGER.warn("Could not load classpath init script: {}", scriptPath);
throw new RuntimeException("Could not load classpath init script: " + scriptPath, e);
} catch (UnsupportedOperationException e) {
LOGGER.error("Error while executing init script: {}", scriptPath, e);
throw new RuntimeException("Error while executing init script: " + scriptPath, e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.testcontainers.containers;

import com.arcadedb.remote.RemoteDatabase;
import org.junit.Test;
import org.testcontainers.utility.DockerImageName;

import static org.assertj.core.api.Assertions.assertThat;

public class ArcadeDBContainerTest {

private static final DockerImageName ARCADEDB_IMAGE = DockerImageName
.parse("arcadedata/arcadedb:24.4.1")
.asCompatibleSubstituteFor("arcadedb");

@Test
public void shouldReturnTheSameSession() {
try ( // container {
ArcadeDBContainer arcadedb = new ArcadeDBContainer("arcadedata/arcadedb:25.3.2")
// }
) {
arcadedb.start();

final RemoteDatabase database = arcadedb.getDatabase();
final RemoteDatabase database2 = arcadedb.getDatabase();

assertThat(database).isSameAs(database2);
}
}

@Test
public void shouldInitializeWithCommands() {
try (ArcadeDBContainer arcadedb = new ArcadeDBContainer(ARCADEDB_IMAGE)) {
arcadedb.start();

final RemoteDatabase db = arcadedb.getDatabase();

db.command("sql", "create vertex type Person");
db.command("sql", "INSERT INTO Person set name='john'");
db.command("sql", "INSERT INTO Person set name='jane'");

assertThat(db.query("sql", "SELECT FROM Person").stream()).hasSize(2);
}
}

@Test
public void shouldInitializeDatabaseFromScript() {
try (
ArcadeDBContainer arcadedb = new ArcadeDBContainer(ARCADEDB_IMAGE)
.withScriptPath("initscript.sql")
.withDatabaseName("persons")
) {
arcadedb.start();

final RemoteDatabase database = arcadedb.getDatabase();

assertThat(database.query("sql", "SELECT FROM Person").stream()).hasSize(4);
}
}
}
6 changes: 6 additions & 0 deletions modules/arcadedb/src/test/resources/initscript.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE VERTEX Type Person;

INSERT INTO Person set name="john";
INSERT INTO Person set name="paul";
INSERT INTO Person set name="luke";
INSERT INTO Person set name="albert";
18 changes: 18 additions & 0 deletions modules/arcadedb/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

<logger name="org.testcontainers" level="INFO"/>
<logger name="org.testcontainers.shaded" level="WARN"/>

</configuration>