1616 */
1717package 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 ;
2222import org .slf4j .Logger ;
2323import 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 ;
2738import java .util .regex .Matcher ;
2839import 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