Skip to content

Commit d398b5b

Browse files
committed
feat: provide multitenancy runtime
1 parent 4db8aea commit d398b5b

File tree

14 files changed

+461
-4
lines changed

14 files changed

+461
-4
lines changed
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Multitenancy
2+
3+
This extension provide support for a multi-tenant EDC.
4+
5+
## TL;DR
6+
Please take a look at the [related sample](../../samples/sample-multi-tenancy)
7+
8+
## How to use
9+
The module provides an extension of the `BaseRuntime` class called `MultiTenantRuntime`.
10+
Setting this as the main class will make the application look for a properties file which path can be specified with
11+
the `edc.tenants.path` property.
12+
13+
In this file the tenants are defined through settings, e.g.:
14+
```properties
15+
edc.tenants.tenant1.edc.fs.config=/config/path
16+
edc.tenants.tenant2.edc.fs.config=/config/path
17+
edc.tenants.tenant3.edc.fs.config=/config/path
18+
```
19+
20+
Using this file the EDC will run with 3 tenants: `tenant1`, `tenant2` and `tenant3`, every one with their respective
21+
configuration file.
22+
Everything that stays after the tenant name in the setting key will be loaded in the tenant runtime, so *theoretically*
23+
(but not recommended) you could define all the tenants configuration in the tenants properties file:
24+
```properties
25+
edc.tenants.tenant1.web.http.port=18181
26+
edc.tenants.tenant1.any.other.setting=value
27+
edc.tenants.tenant2.web.http.port=28181
28+
edc.tenants.tenant3.web.http.port=38181
29+
```

edc-extensions/multi-tenancy/pom.xml

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
4+
5+
This program and the accompanying materials are made available under the
6+
terms of the Apache License, Version 2.0 which is available at
7+
https://www.apache.org/licenses/LICENSE-2.0
8+
9+
SPDX-License-Identifier: Apache-2.0
10+
11+
Contributors:
12+
Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - Initial POM
13+
14+
-->
15+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
16+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
17+
<parent>
18+
<artifactId>edc-extensions</artifactId>
19+
<groupId>net.catenax.edc.extensions</groupId>
20+
<version>0.1.3-SNAPSHOT</version>
21+
</parent>
22+
<modelVersion>4.0.0</modelVersion>
23+
24+
<artifactId>multi-tenancy</artifactId>
25+
<packaging>jar</packaging>
26+
27+
<properties>
28+
<sonar.moduleKey>${project.groupId}_${project.artifactId}</sonar.moduleKey>
29+
</properties>
30+
31+
<build>
32+
<resources>
33+
<resource>
34+
<directory>src/main/resources</directory>
35+
<includes>
36+
<include>**/*</include>
37+
</includes>
38+
</resource>
39+
<resource>
40+
<directory>../..</directory>
41+
<targetPath>META-INF</targetPath>
42+
<includes>
43+
<include>NOTICE.md</include>
44+
<include>LICENSE</include>
45+
</includes>
46+
</resource>
47+
</resources>
48+
</build>
49+
50+
<dependencies>
51+
<dependency>
52+
<groupId>org.eclipse.dataspaceconnector</groupId>
53+
<artifactId>core-boot</artifactId>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>org.junit.jupiter</groupId>
58+
<artifactId>junit-jupiter</artifactId>
59+
<scope>test</scope>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.mockito</groupId>
63+
<artifactId>mockito-core</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>org.eclipse.dataspaceconnector</groupId>
68+
<artifactId>core-base</artifactId>
69+
<scope>test</scope>
70+
</dependency>
71+
</dependencies>
72+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - Initial implementation
12+
*
13+
*/
14+
15+
package org.eclipse.tractusx.edc.boot.runtime;
16+
17+
import static java.lang.ClassLoader.getSystemClassLoader;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.net.MalformedURLException;
22+
import java.net.URL;
23+
import java.net.URLClassLoader;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.util.Arrays;
27+
import java.util.Properties;
28+
import org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime;
29+
import org.eclipse.dataspaceconnector.spi.EdcException;
30+
import org.eclipse.dataspaceconnector.spi.monitor.Monitor;
31+
import org.eclipse.dataspaceconnector.spi.system.configuration.Config;
32+
import org.eclipse.dataspaceconnector.spi.system.configuration.ConfigFactory;
33+
import org.jetbrains.annotations.NotNull;
34+
35+
public class MultiTenantRuntime extends BaseRuntime {
36+
37+
private final @NotNull Monitor monitor = createMonitor();
38+
39+
public static void main(String[] args) {
40+
var runtime = new MultiTenantRuntime();
41+
runtime.boot();
42+
}
43+
44+
protected void boot() {
45+
loadTenantsConfig().getConfig("edc.tenants").partition().forEach(this::bootTenant);
46+
}
47+
48+
private void bootTenant(Config tenantConfig) {
49+
var baseProperties = System.getProperties();
50+
tenantConfig.getRelativeEntries().forEach(System::setProperty);
51+
var entries =
52+
Arrays.stream(System.getProperty("java.class.path").split(File.pathSeparator))
53+
.map(this::toUrl)
54+
.toArray(URL[]::new);
55+
56+
try (var classLoader = URLClassLoader.newInstance(entries, getSystemClassLoader())) {
57+
var runtimeThread =
58+
new Thread(
59+
() -> {
60+
try {
61+
Thread.currentThread().setContextClassLoader(classLoader);
62+
super.boot();
63+
} catch (Exception e) {
64+
throw new EdcException(e);
65+
}
66+
});
67+
68+
monitor.info("Starting tenant " + tenantConfig.currentNode());
69+
runtimeThread.start();
70+
71+
runtimeThread.join(20_000);
72+
73+
} catch (InterruptedException | IOException e) {
74+
throw new EdcException(e);
75+
} finally {
76+
System.setProperties(baseProperties);
77+
}
78+
}
79+
80+
@NotNull
81+
private Config loadTenantsConfig() {
82+
var tenantsPath = System.getProperty("edc.tenants.path");
83+
if (tenantsPath == null) {
84+
throw new EdcException("No edc.tenants.path mandatory property provided");
85+
}
86+
try (var is = Files.newInputStream(Path.of(tenantsPath))) {
87+
var properties = new Properties();
88+
properties.load(is);
89+
return ConfigFactory.fromProperties(properties);
90+
} catch (IOException e) {
91+
throw new EdcException(e);
92+
}
93+
}
94+
95+
private URL toUrl(String entry) {
96+
try {
97+
return new File(entry).toURI().toURL();
98+
} catch (MalformedURLException e) {
99+
throw new RuntimeException(e);
100+
}
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - Initial implementation
12+
*
13+
*/
14+
15+
package org.eclipse.tractusx.edc.boot.runtime;
16+
17+
import static org.junit.jupiter.api.Assertions.*;
18+
19+
import java.util.concurrent.atomic.AtomicInteger;
20+
import org.eclipse.dataspaceconnector.spi.EdcException;
21+
import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext;
22+
import org.junit.jupiter.api.Test;
23+
24+
class MultiTenantRuntimeTest {
25+
26+
private final AtomicInteger initializationCount = new AtomicInteger(0);
27+
private final MultiTenantRuntime runtime =
28+
new MultiTenantRuntime() {
29+
@Override
30+
protected void initializeContext(ServiceExtensionContext context) {
31+
initializationCount.addAndGet(1);
32+
super.initializeContext(context);
33+
}
34+
};
35+
36+
@Test
37+
void throwsExceptionIfNoTenantsPropertyProvided() {
38+
assertThrows(EdcException.class, runtime::boot);
39+
assertEquals(0, initializationCount.get());
40+
}
41+
42+
@Test
43+
void throwsExceptionIfTenantsFileDoesNotExist() {
44+
System.setProperty("edc.tenants.path", "unexistentfile");
45+
46+
assertThrows(EdcException.class, runtime::boot);
47+
assertEquals(0, initializationCount.get());
48+
}
49+
50+
@Test
51+
void threadForEveryTenant() {
52+
int threadsBefore = Thread.activeCount();
53+
System.setProperty("edc.tenants.path", "./src/test/resources/tenants.properties");
54+
55+
runtime.boot();
56+
57+
assertTrue(Thread.activeCount() > threadsBefore);
58+
assertEquals(2, initializationCount.get());
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
edc.tenants.one.edc.any=any
2+
edc.tenants.two.edc.any=any

edc-extensions/pom.xml

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@
3131

3232
<modules>
3333
<module>business-partner-validation</module>
34-
<module>postgresql-migration</module>
34+
<module>cx-oauth2</module>
35+
<module>data-encryption</module>
36+
<module>dataplane-selector-configuration</module>
3537
<module>hashicorp-vault</module>
38+
<module>multi-tenancy</module>
39+
<module>postgresql-migration</module>
3640
<module>xsuaa-authenticator</module>
37-
<module>dataplane-selector-configuration</module>
38-
<module>data-encryption</module>
39-
<module>cx-oauth2</module>
4041
</modules>
4142

4243
</project>

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<module>edc-controlplane</module>
2929
<module>edc-dataplane</module>
3030
<module>edc-tests</module>
31+
<module>samples</module>
3132
</modules>
3233

3334
<inceptionYear>2022</inceptionYear>
@@ -355,6 +356,11 @@
355356
<artifactId>cx-oauth2</artifactId>
356357
<version>${project.version}</version>
357358
</dependency>
359+
<dependency>
360+
<groupId>net.catenax.edc.extensions</groupId>
361+
<artifactId>multi-tenancy</artifactId>
362+
<version>${project.version}</version>
363+
</dependency>
358364

359365
<!-- CX Control-/Data-Plane modules -->
360366
<dependency>

samples/pom.xml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH
4+
5+
This program and the accompanying materials are made available under the
6+
terms of the Apache License, Version 2.0 which is available at
7+
https://www.apache.org/licenses/LICENSE-2.0
8+
9+
SPDX-License-Identifier: Apache-2.0
10+
11+
Contributors:
12+
Mercedes-Benz Tech Innovation GmbH - Initial POM
13+
14+
-->
15+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
16+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
17+
<parent>
18+
<groupId>net.catenax.edc</groupId>
19+
<artifactId>product-edc-parent</artifactId>
20+
<version>0.1.3-SNAPSHOT</version>
21+
</parent>
22+
<modelVersion>4.0.0</modelVersion>
23+
24+
<groupId>net.catenax.edc.samples</groupId>
25+
<artifactId>samples</artifactId>
26+
<packaging>pom</packaging>
27+
28+
<properties>
29+
<sonar.moduleKey>${project.groupId}_${project.artifactId}</sonar.moduleKey>
30+
</properties>
31+
32+
<modules>
33+
<module>sample-multi-tenancy</module>
34+
</modules>
35+
36+
</project>
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Sample Multi Tenancy
2+
3+
Build:
4+
```shell
5+
./mvnw -pl samples/sample-multi-tenancy -am package
6+
```
7+
8+
Run:
9+
```shell
10+
java -jar -Dedc.tenants.path=samples/sample-multi-tenancy/tenants.properties samples/sample-multi-tenancy/target/sample-multi-tenancy.jar
11+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
web.http.port=18181
2+
web.http.path=/api
3+
web.http.ids.port=18282
4+
web.http.ids.path=/api/v1/ids
5+
ids.webhook.address=http://localhost:18282
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
web.http.port=28181
2+
web.http.path=/api
3+
web.http.ids.port=28282
4+
web.http.ids.path=/api/v1/ids
5+
ids.webhook.address=http://localhost:28282
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
web.http.port=38181
2+
web.http.path=/api
3+
web.http.ids.port=38282
4+
web.http.ids.path=/api/v1/ids
5+
ids.webhook.address=http://localhost:38282

0 commit comments

Comments
 (0)