Skip to content

Commit 662a06a

Browse files
committed
Refactoring; implemented more reasonable waiting strategy for container startup.
1 parent 999d0c1 commit 662a06a

File tree

4 files changed

+134
-200
lines changed

4 files changed

+134
-200
lines changed

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

Lines changed: 2 additions & 5 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;
@@ -36,12 +35,10 @@
3635
*/
3736
public class OakDocumentRDBRepositoryStub extends BaseRepositoryStub {
3837

38+
private static final String jdbcUrl = RdbUtils.mapJdbcURL();
3939

4040
private final Repository repository;
4141

42-
private static final String fname = (new File("target")).isDirectory() ? "target/" : "";
43-
private static final String jdbcUrl = RdbUtils.mapJdbcURL().replace("{fname}", fname);
44-
4542
/**
4643
* Constructor as required by the JCR TCK.
4744
*
@@ -79,7 +76,7 @@ public void run() {
7976

8077
public static boolean isAvailable() {
8178
try {
82-
Connection c = DriverManager.getConnection(OakDocumentRDBRepositoryStub.jdbcUrl, RdbUtils.USERNAME, RdbUtils.PASSWD);
79+
Connection c = DriverManager.getConnection(jdbcUrl, RdbUtils.USERNAME, RdbUtils.PASSWD);
8380
c.close();
8481
return true;
8582
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,12 @@ public class DocumentRdbFixture extends NodeStoreFixture {
4141

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

44-
private String jdbcUrl;
45-
46-
private final String fname = (new File("target")).isDirectory() ? "target/" : "";
44+
private static final String jdbcUrl = RdbUtils.mapJdbcURL();
4745

4846
@Override
4947
public NodeStore createNodeStore() {
5048
String prefix = "T" + Long.toHexString(System.currentTimeMillis());
5149
RDBOptions options = new RDBOptions().tablePrefix(prefix).dropTablesOnClose(true);
52-
this.jdbcUrl = RdbUtils.mapJdbcURL().replace("{fname}", fname);
5350
DataSource ds = RDBDataSourceFactory.forJdbcUrl(jdbcUrl, RdbUtils.USERNAME, RdbUtils.PASSWD);
5451
//do not reuse the whiteboard
5552
setWhiteboard(new DefaultWhiteboard());

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

Lines changed: 131 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,25 @@
1616
*/
1717
package org.apache.jackrabbit.oak.plugins.document;
1818

19-
import org.apache.jackrabbit.oak.plugins.document.rdb.RdbDockerRule;
20-
import org.junit.runner.Description;
21-
import org.junit.runners.model.Statement;
19+
import com.github.dockerjava.api.DockerClient;
20+
import org.apache.jackrabbit.guava.common.base.Strings;
21+
import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
24+
import org.testcontainers.DockerClientFactory;
25+
import org.testcontainers.containers.GenericContainer;
26+
import org.testcontainers.containers.startupcheck.StartupCheckStrategy;
27+
import org.testcontainers.images.RemoteDockerImage;
28+
import org.testcontainers.utility.DockerImageName;
2429

25-
import java.util.concurrent.atomic.AtomicInteger;
26-
import java.util.concurrent.atomic.AtomicReference;
30+
import javax.sql.DataSource;
31+
import java.io.File;
32+
import java.sql.Connection;
33+
import java.sql.SQLException;
34+
import java.time.Duration;
35+
import java.time.Instant;
36+
import java.util.concurrent.TimeUnit;
37+
import java.util.concurrent.TimeoutException;
2738
import java.util.regex.Matcher;
2839
import java.util.regex.Pattern;
2940

@@ -34,42 +45,138 @@ public class RdbUtils {
3445
public static final String URL = System.getProperty("rdb.jdbc-url", "jdbc:h2:file:./{fname}oaktest;DB_CLOSE_ON_EXIT=FALSE");
3546
public static final String USERNAME = System.getProperty("rdb.jdbc-user", "sa");
3647
public static final String PASSWD = System.getProperty("rdb.jdbc-passwd", "");
37-
public static final String IMAGE = System.getProperty("rdb.docker-image", "");
48+
public static final String IMG = System.getProperty("rdb.docker-image", "");
3849

39-
private static AtomicInteger port = new AtomicInteger(-1);
40-
private static AtomicReference<String> host = new AtomicReference<>("localhost");
50+
private static final boolean RDB_AVAILABLE;
51+
private static GenericContainer<?> rdbContainer;
52+
53+
private static int exposedPort = getPortFromJdbcURL(URL);
4154

4255
static {
56+
boolean dockerAvailable = false;
57+
boolean imageAvailable = false;
4358
try {
44-
if (RdbDockerRule.isDockerImageAvailable()) {
45-
RdbDockerRule rule = new RdbDockerRule();
46-
rule.apply(new Statement() {
47-
@Override
48-
public void evaluate() {
49-
port.set(rule.getMappedPort());
50-
}
51-
}, Description.EMPTY).evaluate();
59+
dockerAvailable = checkDockerAvailability();
60+
if (dockerAvailable) {
61+
imageAvailable = checkImageAvailability();
62+
} else {
63+
LOG.info("docker not available");
5264
}
5365
} catch (Throwable t) {
54-
LOG.debug("Failed to initialize docker container", t);
66+
LOG.error("not able to pull specified docker image: {}, error: ", IMG, t);
67+
}
68+
RDB_AVAILABLE = dockerAvailable && imageAvailable;
69+
if (RDB_AVAILABLE) {
70+
LOG.error("New container");
71+
rdbContainer = new GenericContainer<>(DockerImageName.parse(IMG))
72+
.withPrivilegedMode(true)
73+
.withExposedPorts(exposedPort)
74+
.withStartupTimeout(Duration.ofMinutes(15));
75+
// .withStartupCheckStrategy(new StartupCheckStrategy() {
76+
// @Override
77+
// public StartupStatus checkStartupState(DockerClient dockerClient, String s) {
78+
// LOG.error("checkStartupState#1");
79+
// Connection connection = null;
80+
// try {
81+
// String url = RdbUtils.mapJdbcURL();
82+
// LOG.error("checkStartupState#2");
83+
// DataSource dataSource = RDBDataSourceFactory.forJdbcUrl(url, RdbUtils.USERNAME, RdbUtils.PASSWD);
84+
// connection = dataSource.getConnection();
85+
// LOG.error("checkStartupState#3");
86+
// } catch (Throwable expected) {
87+
// LOG.error("checkStartupState#4");
88+
// return StartupStatus.NOT_YET_KNOWN;
89+
// } finally {
90+
// if (connection != null) {
91+
// try {
92+
// connection.close();
93+
// } catch (SQLException expected) {}
94+
// }
95+
// }
96+
// LOG.error("checkStartupState#5");
97+
//
98+
// return StartupStatus.SUCCESSFUL;
99+
// }
100+
// }.withTimeout(Duration.ofMinutes(10)));
101+
102+
103+
try {
104+
long startTime = Instant.now().toEpochMilli();
105+
rdbContainer.start();
106+
LOG.info("RDB container started in: " + (Instant.now().toEpochMilli() - startTime) + " ms");
107+
String url = RdbUtils.mapJdbcURL();
108+
LOG.info("Mapped JDBC URL is {}.", url);
109+
boolean containerReady = false;
110+
LOG.info("Trying to connect to {}", url);
111+
for (int k = 0; k < 30 && !containerReady; k++) {
112+
Thread.sleep(10000);
113+
Connection connection = null;
114+
try {
115+
DataSource dataSource = RDBDataSourceFactory.forJdbcUrl(url, RdbUtils.USERNAME, RdbUtils.PASSWD);
116+
connection = dataSource.getConnection();
117+
containerReady = true;
118+
} catch (SQLException expected) {
119+
LOG.info("Failed to connect to {}, will retry", url);
120+
} finally {
121+
if (connection != null) {
122+
try {
123+
connection.close();
124+
} catch (SQLException expected) {}
125+
}
126+
}
127+
if (containerReady) {
128+
LOG.info("Container ready");
129+
} else {
130+
LOG.error("Failed to connect to {} within timeout", url);
131+
}
132+
}
133+
} catch (Exception e) {
134+
LOG.error("error while starting RDB container, error: ", e);
135+
}
55136
}
56137
}
57138

58-
public static String mapJdbcURL() {
59-
return mapJdbcURL(URL);
139+
public static int getPortFromJdbcURL(String jdbcURL) {
140+
String normalizedJdbcUri = jdbcURL.replaceFirst("@//", "//").replaceFirst("@", "//");
141+
Pattern pattern = Pattern.compile("//[^:/]+(:(\\d+))?");
142+
Matcher matcher = pattern.matcher(normalizedJdbcUri);
143+
if (matcher.find()) {
144+
if (matcher.groupCount() > 1) {
145+
try {
146+
return Integer.parseInt(matcher.group(2));
147+
} catch (NumberFormatException ignored) {
148+
//should not happen
149+
}
150+
}
151+
}
152+
return -1;
60153
}
61154

62-
public static String mapJdbcURL(String jdbcURL) {
63-
if (port.get() > -1) {
64-
String normalizedJdbcUri = jdbcURL.replaceFirst("@//", "//").replaceFirst("@", "//");
155+
public static String mapJdbcURL() {
156+
String jdbcUrl = URL;
157+
if (RDB_AVAILABLE) {
158+
String normalizedJdbcUri = URL.replaceFirst("@//", "//").replaceFirst("@", "//");
65159
Pattern pattern = Pattern.compile("//[^:/]+(:(\\d+))?");
66160
Matcher matcher = pattern.matcher(normalizedJdbcUri);
67161
if (matcher.find()) {
68162
if (matcher.groupCount() > 1) {
69-
return matcher.replaceFirst("//" + host + ":" + port);
163+
jdbcUrl = matcher.replaceFirst("//" + rdbContainer.getHost() + ":" + rdbContainer.getMappedPort(exposedPort));
70164
}
71165
}
72166
}
73-
return jdbcURL;
167+
return jdbcUrl.replace("{fname}", (new File("target")).isDirectory() ? "target/" : "");
168+
}
169+
170+
private static boolean checkImageAvailability() throws TimeoutException {
171+
if (Strings.isNullOrEmpty(IMG)) {
172+
return false;
173+
}
174+
RemoteDockerImage remoteDockerImage = new RemoteDockerImage(DockerImageName.parse(IMG));
175+
remoteDockerImage.get(60, TimeUnit.MINUTES);
176+
return true;
177+
}
178+
179+
private static boolean checkDockerAvailability() {
180+
return DockerClientFactory.instance().isDockerAvailable();
74181
}
75182
}

0 commit comments

Comments
 (0)