Skip to content

Commit 8712457

Browse files
rnorthjustinwyer
andauthored
Refactor JDBC URL parsing to support different Oracle URLs (#4476)
Detects SID `:` or service name `/` in the supplied TC JDBC URL. In addition, the affected regex patterns have been refactored to use named groups. Co-authored-by: Justin Wyer <[email protected]>
1 parent 0ad8154 commit 8712457

File tree

2 files changed

+61
-23
lines changed

2 files changed

+61
-23
lines changed

modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import java.util.Optional;
88
import java.util.regex.Matcher;
99
import java.util.regex.Pattern;
10-
import java.util.stream.Collector;
1110
import java.util.stream.Collectors;
1211
import java.util.stream.Stream;
12+
1313
import lombok.AllArgsConstructor;
1414
import lombok.EqualsAndHashCode;
1515
import lombok.Getter;
@@ -97,33 +97,33 @@ private void parseUrl() {
9797
throw new IllegalArgumentException("JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified");
9898
}
9999
}
100-
databaseType = urlMatcher.group(1);
100+
databaseType = urlMatcher.group("databaseType");
101101

102-
imageTag = Optional.ofNullable(urlMatcher.group(3));
102+
imageTag = Optional.ofNullable(urlMatcher.group("imageTag"));
103103

104104
//String like hostname:port/database name, which may vary based on target database.
105105
//Clients can further parse it as needed.
106-
dbHostString = urlMatcher.group(4);
106+
dbHostString = urlMatcher.group("dbHostString");
107107

108108
//In case it matches to the default pattern
109109
Matcher dbInstanceMatcher = Patterns.DB_INSTANCE_MATCHING_PATTERN.matcher(dbHostString);
110110
if (dbInstanceMatcher.matches()) {
111-
databaseHost = Optional.of(dbInstanceMatcher.group(1));
112-
databasePort = Optional.ofNullable(dbInstanceMatcher.group(3)).map(value -> Integer.valueOf(value));
113-
databaseName = Optional.of(dbInstanceMatcher.group(4));
111+
databaseHost = Optional.of(dbInstanceMatcher.group("databaseHost"));
112+
databasePort = Optional.ofNullable(dbInstanceMatcher.group("databasePort")).map(Integer::valueOf);
113+
databaseName = Optional.of(dbInstanceMatcher.group("databaseName"));
114114
}
115115

116116
queryParameters = Collections.unmodifiableMap(
117117
parseQueryParameters(
118-
Optional.ofNullable(urlMatcher.group(5)).orElse("")));
118+
Optional.ofNullable(urlMatcher.group("queryParameters")).orElse("")));
119119

120120
String query = queryParameters
121121
.entrySet()
122122
.stream()
123123
.map(e -> e.getKey() + "=" + e.getValue())
124124
.collect(Collectors.joining("&"));
125125

126-
if (query == null || query.trim().length() == 0) {
126+
if (query.trim().length() == 0) {
127127
queryString = Optional.empty();
128128
} else {
129129
queryString = Optional.of("?" + query);
@@ -149,7 +149,7 @@ private void parseUrl() {
149149
}
150150

151151
private Map<String, String> parseTmpfsOptions(Map<String, String> containerParameters) {
152-
if(!containerParameters.containsKey("TC_TMPFS")){
152+
if (!containerParameters.containsKey("TC_TMPFS")) {
153153
return Collections.emptyMap();
154154
}
155155

@@ -209,26 +209,59 @@ public Map<String, String> getTmpfsOptions() {
209209
* @author manikmagar
210210
*/
211211
public interface Patterns {
212-
Pattern URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z0-9]+)(:([^:]+))?://([^\\?]+)(\\?.*)?");
213-
214-
Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile("jdbc:tc:([a-z]+)(:([^(thin:)]+))?:thin:@([^\\?]+)(\\?.*)?");
212+
Pattern URL_MATCHING_PATTERN = Pattern.compile(
213+
"jdbc:tc:" +
214+
"(?<databaseType>[a-z0-9]+)" +
215+
"(:(?<imageTag>[^:]+))?" +
216+
"://" +
217+
"(?<dbHostString>[^?]+)" +
218+
"(?<queryParameters>\\?.*)?"
219+
);
220+
221+
Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile(
222+
"jdbc:tc:" +
223+
"(?<databaseType>[a-z]+)" +
224+
"(:(?<imageTag>(?!thin).+))?:thin:(//)?" +
225+
"(" +
226+
"(?<username>[^:" +
227+
"?^/]+)/(?<password>[^?^/]+)" +
228+
")?" +
229+
"@" +
230+
"(?<dbHostString>[^?]+)" +
231+
"(?<queryParameters>\\?.*)?"
232+
);
215233

216234
//Matches to part of string - hostname:port/databasename
217-
Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile("([^:]+)(:([0-9]+))?/([^\\\\?]+)");
218-
219-
Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_DAEMON=([^\\?&]+).*");
220-
Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITSCRIPT=([^\\?&]+).*");
221-
Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([\\?&]?)TC_INITFUNCTION=" +
235+
Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile(
236+
"(?<databaseHost>[^:]+)" +
237+
"(:(?<databasePort>[0-9]+))?" +
238+
"(" +
239+
"(?<sidOrServiceName>[:/])" +
240+
"|" +
241+
";databaseName=" +
242+
")" +
243+
"(?<databaseName>[^\\\\?]+)"
244+
);
245+
246+
Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_DAEMON=([^?&]+).*");
247+
248+
/**
249+
* @deprecated for removal
250+
*/
251+
@Deprecated
252+
Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITSCRIPT=([^?&]+).*");
253+
254+
Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITFUNCTION=" +
222255
"((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" +
223256
"::" +
224257
"(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" +
225258
".*");
226259

227260
String TC_PARAM_NAME_PATTERN = "(TC_[A-Z_]+)";
228261

229-
Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^\\?&]+)");
262+
Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^?&]+)");
230263

231-
Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^\\?&=]+)=([^\\?&]*)");
264+
Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^?&=]+)=([^?&]*)");
232265

233266
}
234267

modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,17 @@ public static Iterable<Object[]> data() {
3737
{"jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test"},
3838
{"jdbc:tc:postgresql:1.2.3://hostname/test", "postgresql", Optional.of("1.2.3"), "hostname/test", "test"},
3939
{"jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test"},
40-
{"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", ""},
41-
{"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", ""},
40+
{"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"},
41+
{"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"},
4242
{"jdbc:tc:mariadb:1.2.3://localhost:3306/test", "mariadb", Optional.of("1.2.3"), "localhost:3306/test", "test"},
4343
{"jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test"},
44+
{"jdbc:tc:oracle:1.2.3:thin://@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"},
4445
{"jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"},
45-
{"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"}
46+
{"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"},
47+
{"jdbc:tc:oracle:1.2.3:thin:@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"},
48+
{"jdbc:tc:oracle:1.2.3:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"},
49+
{"jdbc:tc:oracle:1.2.3-anything:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3-anything"), "localhost:1521:test", "test"},
50+
{"jdbc:tc:oracle:thin:@localhost:1521:test", "oracle", Optional.empty(), "localhost:1521:test", "test"}
4651
});
4752
}
4853

0 commit comments

Comments
 (0)