Skip to content

Commit fe9c04c

Browse files
authored
OAK-11364: For oak-jcr tests, support automatic starting/stopping of … (#1961)
done
1 parent 321a13f commit fe9c04c

File tree

5 files changed

+161
-26
lines changed

5 files changed

+161
-26
lines changed

oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/OakDocumentRDBRepositoryStub.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.jackrabbit.oak.jcr;
1818

19-
import java.io.File;
2019
import java.sql.Connection;
2120
import java.sql.DriverManager;
2221
import java.sql.SQLException;
@@ -26,6 +25,7 @@
2625
import javax.jcr.RepositoryException;
2726

2827
import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
28+
import org.apache.jackrabbit.oak.plugins.document.RdbConnectionUtils;
2929
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
3030
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentNodeStoreBuilder;
3131
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions;
@@ -35,17 +35,10 @@
3535
*/
3636
public class OakDocumentRDBRepositoryStub extends BaseRepositoryStub {
3737

38-
protected static final String URL = System.getProperty("rdb.jdbc-url", "jdbc:h2:file:./{fname}oaktest;DB_CLOSE_ON_EXIT=FALSE");
39-
40-
protected static final String USERNAME = System.getProperty("rdb.jdbc-user", "sa");
41-
42-
protected static final String PASSWD = System.getProperty("rdb.jdbc-passwd", "");
38+
private static final String jdbcUrl = RdbConnectionUtils.mapJdbcURL();
4339

4440
private final Repository repository;
4541

46-
private static final String fname = (new File("target")).isDirectory() ? "target/" : "";
47-
private static final String jdbcUrl = URL.replace("{fname}", fname);
48-
4942
/**
5043
* Constructor as required by the JCR TCK.
5144
*
@@ -64,7 +57,7 @@ public OakDocumentRDBRepositoryStub(Properties settings) throws RepositoryExcept
6457
m = new RDBDocumentNodeStoreBuilder().
6558
memoryCacheSize(64 * 1024 * 1024).
6659
setPersistentCache("target/persistentCache,time").
67-
setRDBConnection(RDBDataSourceFactory.forJdbcUrl(jdbcUrl, USERNAME, PASSWD), options).
60+
setRDBConnection(RDBDataSourceFactory.forJdbcUrl(jdbcUrl, RdbConnectionUtils.USERNAME, RdbConnectionUtils.PASSWD), options).
6861
build();
6962
Jcr jcr = new Jcr(m);
7063
preCreateRepository(jcr);
@@ -83,7 +76,7 @@ public void run() {
8376

8477
public static boolean isAvailable() {
8578
try {
86-
Connection c = DriverManager.getConnection(OakDocumentRDBRepositoryStub.jdbcUrl, USERNAME, PASSWD);
79+
Connection c = DriverManager.getConnection(jdbcUrl, RdbConnectionUtils.USERNAME, RdbConnectionUtils.PASSWD);
8780
c.close();
8881
return true;
8982
}

oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentRdbFixture.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.apache.jackrabbit.oak.fixture;
2121

2222
import java.io.Closeable;
23-
import java.io.File;
2423
import java.io.IOException;
2524
import java.util.Map;
2625
import java.util.Objects;
@@ -29,6 +28,7 @@
2928
import javax.sql.DataSource;
3029

3130
import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
31+
import org.apache.jackrabbit.oak.plugins.document.RdbConnectionUtils;
3232
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
3333
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentNodeStoreBuilder;
3434
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions;
@@ -40,22 +40,13 @@ public class DocumentRdbFixture extends NodeStoreFixture {
4040

4141
private final Map<NodeStore, DataSource> dataSources = new ConcurrentHashMap<NodeStore, DataSource>();
4242

43-
private String jdbcUrl;
44-
45-
private final String fname = (new File("target")).isDirectory() ? "target/" : "";
46-
47-
private final String pUrl = System.getProperty("rdb.jdbc-url", "jdbc:h2:file:./{fname}oaktest");
48-
49-
private final String pUser = System.getProperty("rdb.jdbc-user", "sa");
50-
51-
private final String pPasswd = System.getProperty("rdb.jdbc-passwd", "");
43+
private static final String jdbcUrl = RdbConnectionUtils.mapJdbcURL();
5244

5345
@Override
5446
public NodeStore createNodeStore() {
5547
String prefix = "T" + Long.toHexString(System.currentTimeMillis());
5648
RDBOptions options = new RDBOptions().tablePrefix(prefix).dropTablesOnClose(true);
57-
this.jdbcUrl = pUrl.replace("{fname}", fname);
58-
DataSource ds = RDBDataSourceFactory.forJdbcUrl(jdbcUrl, pUser, pPasswd);
49+
DataSource ds = RDBDataSourceFactory.forJdbcUrl(jdbcUrl, RdbConnectionUtils.USERNAME, RdbConnectionUtils.PASSWD);
5950
//do not reuse the whiteboard
6051
setWhiteboard(new DefaultWhiteboard());
6152
RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder();
@@ -83,6 +74,6 @@ public void dispose(NodeStore nodeStore) {
8374

8475
@Override
8576
public String toString() {
86-
return "DocumentNodeStore[RDB] on " + Objects.toString(this.jdbcUrl, this.pUrl);
77+
return "DocumentNodeStore[RDB] on " + Objects.toString(this.jdbcUrl);
8778
}
8879
}

oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractRDBConnectionTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public static void checkRDBAvailable() {
5151

5252
@Before
5353
public void setUpConnection() throws Exception {
54-
dataSource = RDBDataSourceFactory.forJdbcUrl(URL, USERNAME, PASSWD);
54+
dataSource = RDBDataSourceFactory.forJdbcUrl(RdbConnectionUtils.mapJdbcURL(), USERNAME, PASSWD);
5555
Revision.setClock(getTestClock());
5656
mk = new DocumentMK(newBuilder(dataSource).build());
5757
}

oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CacheConsistencyRDBTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class CacheConsistencyRDBTest extends AbstractRDBConnectionTest {
4545
@Before
4646
@Override
4747
public void setUpConnection() throws Exception {
48-
dataSource = RDBDataSourceFactory.forJdbcUrl(URL, USERNAME, PASSWD);
48+
dataSource = RDBDataSourceFactory.forJdbcUrl(RdbConnectionUtils.mapJdbcURL(), USERNAME, PASSWD);
4949
DocumentMK.Builder builder = new DocumentMK.Builder().clock(getTestClock()).setAsyncDelay(0);
5050
RDBOptions opt = new RDBOptions().tablePrefix("T" + Long.toHexString(System.currentTimeMillis())).dropTablesOnClose(true);
5151
store = new TestStore(dataSource, builder, opt);
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.jackrabbit.oak.plugins.document;
18+
19+
import org.apache.jackrabbit.guava.common.base.Strings;
20+
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.testcontainers.DockerClientFactory;
24+
import org.testcontainers.containers.GenericContainer;
25+
import org.testcontainers.images.RemoteDockerImage;
26+
import org.testcontainers.utility.DockerImageName;
27+
28+
import javax.sql.DataSource;
29+
import java.io.File;
30+
import java.sql.Connection;
31+
import java.sql.SQLException;
32+
import java.time.Duration;
33+
import java.time.Instant;
34+
import java.util.concurrent.TimeUnit;
35+
import java.util.concurrent.TimeoutException;
36+
import java.util.regex.Matcher;
37+
import java.util.regex.Pattern;
38+
39+
public class RdbConnectionUtils {
40+
41+
private static final Logger LOG = LoggerFactory.getLogger(RdbConnectionUtils.class);
42+
43+
public static final String URL = System.getProperty("rdb.jdbc-url", "jdbc:h2:file:./{fname}oaktest;DB_CLOSE_ON_EXIT=FALSE");
44+
public static final String USERNAME = System.getProperty("rdb.jdbc-user", "sa");
45+
public static final String PASSWD = System.getProperty("rdb.jdbc-passwd", "");
46+
public static final String IMG = System.getProperty("rdb.docker-image", "");
47+
48+
private static final boolean RDB_AVAILABLE;
49+
private static GenericContainer<?> rdbContainer;
50+
51+
private static int exposedPort = getPortFromJdbcURL(URL);
52+
53+
static {
54+
boolean dockerAvailable = false;
55+
boolean imageAvailable = false;
56+
try {
57+
dockerAvailable = checkDockerAvailability();
58+
if (dockerAvailable) {
59+
imageAvailable = checkImageAvailability();
60+
} else {
61+
LOG.info("docker not available");
62+
}
63+
} catch (Throwable t) {
64+
LOG.error("not able to pull specified docker image: {}, error: ", IMG, t);
65+
}
66+
RDB_AVAILABLE = dockerAvailable && imageAvailable;
67+
if (RDB_AVAILABLE) {
68+
rdbContainer = new GenericContainer<>(DockerImageName.parse(IMG))
69+
.withPrivilegedMode(true)
70+
.withExposedPorts(exposedPort)
71+
.withStartupTimeout(Duration.ofMinutes(15));
72+
try {
73+
long startTime = Instant.now().toEpochMilli();
74+
rdbContainer.start();
75+
LOG.info("RDB container started in: " + (Instant.now().toEpochMilli() - startTime) + " ms");
76+
String url = RdbConnectionUtils.mapJdbcURL();
77+
LOG.info("Mapped JDBC URL is {}.", url);
78+
boolean containerReady = false;
79+
LOG.info("Trying to connect to {}", url);
80+
for (int k = 0; k < 30 && !containerReady; k++) {
81+
Thread.sleep(10000);
82+
Connection connection = null;
83+
try {
84+
DataSource dataSource = RDBDataSourceFactory.forJdbcUrl(url, RdbConnectionUtils.USERNAME, RdbConnectionUtils.PASSWD);
85+
connection = dataSource.getConnection();
86+
containerReady = true;
87+
} catch (SQLException expected) {
88+
LOG.info("Failed to connect to {}, will retry", url);
89+
} finally {
90+
if (connection != null) {
91+
try {
92+
connection.close();
93+
} catch (SQLException expected) {}
94+
}
95+
}
96+
if (containerReady) {
97+
LOG.info("Container ready");
98+
} else {
99+
LOG.error("Failed to connect to {} within timeout", url);
100+
}
101+
}
102+
} catch (Exception e) {
103+
LOG.error("error while starting RDB container, error: ", e);
104+
}
105+
}
106+
}
107+
108+
public static int getPortFromJdbcURL(String jdbcURL) {
109+
String normalizedJdbcUri = jdbcURL.replaceFirst("@//", "//").replaceFirst("@", "//");
110+
Pattern pattern = Pattern.compile("//[^:/]+(:(\\d+))?");
111+
Matcher matcher = pattern.matcher(normalizedJdbcUri);
112+
if (matcher.find()) {
113+
if (matcher.groupCount() > 1) {
114+
try {
115+
return Integer.parseInt(matcher.group(2));
116+
} catch (NumberFormatException ignored) {
117+
//should not happen
118+
}
119+
}
120+
}
121+
return -1;
122+
}
123+
124+
public static String mapJdbcURL() {
125+
String jdbcUrl = URL;
126+
if (RDB_AVAILABLE) {
127+
String normalizedJdbcUri = URL.replaceFirst("@//", "//").replaceFirst("@", "//");
128+
Pattern pattern = Pattern.compile("//[^:/]+(:(\\d+))?");
129+
Matcher matcher = pattern.matcher(normalizedJdbcUri);
130+
if (matcher.find()) {
131+
if (matcher.groupCount() > 1) {
132+
jdbcUrl = matcher.replaceFirst("//" + rdbContainer.getHost() + ":" + rdbContainer.getMappedPort(exposedPort));
133+
}
134+
}
135+
}
136+
return jdbcUrl.replace("{fname}", (new File("target")).isDirectory() ? "target/" : "");
137+
}
138+
139+
private static boolean checkImageAvailability() throws TimeoutException {
140+
if (Strings.isNullOrEmpty(IMG)) {
141+
return false;
142+
}
143+
RemoteDockerImage remoteDockerImage = new RemoteDockerImage(DockerImageName.parse(IMG));
144+
remoteDockerImage.get(60, TimeUnit.MINUTES);
145+
return true;
146+
}
147+
148+
private static boolean checkDockerAvailability() {
149+
return DockerClientFactory.instance().isDockerAvailable();
150+
}
151+
}

0 commit comments

Comments
 (0)