0
# Container Providers
1
2
The `JdbcDatabaseContainerProvider` system uses the Service Provider Interface (SPI) pattern to create database-specific container implementations based on JDBC URL database types.
3
4
## Capabilities
5
6
### Provider Base Class
7
8
Abstract base class for database container providers that integrate with the JDBC driver's discovery mechanism.
9
10
```java { .api }
11
/**
12
* Base class for classes that can provide a JDBC container
13
* Discovered via ServiceLoader mechanism
14
*/
15
public abstract class JdbcDatabaseContainerProvider {
16
}
17
```
18
19
### Database Type Support
20
21
Method to determine if a provider can handle a specific database type.
22
23
```java { .api }
24
/**
25
* Test if the specified database type is supported by this provider
26
* Should match the base image name from JDBC URL
27
* @param databaseType database type extracted from JDBC URL (e.g., "mysql", "postgresql")
28
* @return true if provider can handle this database type, false otherwise
29
*/
30
public abstract boolean supports(String databaseType);
31
```
32
33
**Usage Example:**
34
```java
35
public class MySQLContainerProvider extends JdbcDatabaseContainerProvider {
36
@Override
37
public boolean supports(String databaseType) {
38
return "mysql".equals(databaseType);
39
}
40
}
41
```
42
43
### Container Instance Creation
44
45
Methods for creating container instances with different configurations.
46
47
```java { .api }
48
/**
49
* Create new container instance with default tag
50
* Subclasses should override to provide stable default instead of "latest"
51
* @return new JdbcDatabaseContainer instance
52
*/
53
public JdbcDatabaseContainer newInstance();
54
55
/**
56
* Create new container instance with specified image tag
57
* @param tag Docker image tag to use
58
* @return new JdbcDatabaseContainer instance
59
*/
60
public abstract JdbcDatabaseContainer newInstance(String tag);
61
62
/**
63
* Create new container instance using ConnectionUrl information
64
* Automatically extracts image tag and reusability settings from URL
65
* @param url parsed ConnectionUrl with configuration
66
* @return configured JdbcDatabaseContainer instance
67
*/
68
public JdbcDatabaseContainer newInstance(ConnectionUrl url);
69
```
70
71
**Implementation Example:**
72
```java
73
public class PostgreSQLContainerProvider extends JdbcDatabaseContainerProvider {
74
@Override
75
public boolean supports(String databaseType) {
76
return "postgresql".equals(databaseType);
77
}
78
79
@Override
80
public JdbcDatabaseContainer newInstance() {
81
return newInstance("13"); // Use stable default instead of "latest"
82
}
83
84
@Override
85
public JdbcDatabaseContainer newInstance(String tag) {
86
return new PostgreSQLContainer<>("postgres:" + tag);
87
}
88
}
89
```
90
91
### Helper Method for URL-based Creation
92
93
Protected helper method for creating instances from connection URLs with standard parameter mapping.
94
95
```java { .api }
96
/**
97
* Helper method to create container instance from ConnectionUrl
98
* Handles common parameter extraction and container configuration
99
* @param connectionUrl parsed connection URL
100
* @param userParamName query parameter name for username (e.g., "user")
101
* @param pwdParamName query parameter name for password (e.g., "password")
102
* @return configured JdbcDatabaseContainer instance
103
* @throws NullPointerException if connectionUrl is null
104
*/
105
protected JdbcDatabaseContainer newInstanceFromConnectionUrl(
106
ConnectionUrl connectionUrl,
107
String userParamName,
108
String pwdParamName
109
);
110
```
111
112
**Usage in Provider Implementation:**
113
```java
114
public class MySQLContainerProvider extends JdbcDatabaseContainerProvider {
115
@Override
116
public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) {
117
// Use helper method with MySQL-specific parameter names
118
return newInstanceFromConnectionUrl(connectionUrl, "user", "password");
119
}
120
}
121
```
122
123
## Service Provider Interface (SPI) Integration
124
125
### ServiceLoader Discovery
126
127
The JDBC driver discovers providers using Java's ServiceLoader mechanism:
128
129
1. Provider implementations must extend `JdbcDatabaseContainerProvider`
130
2. Provider classes must be listed in `META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider`
131
3. At runtime, ServiceLoader loads all available providers
132
4. Driver queries each provider with `supports()` method to find matches
133
134
### Provider Registration
135
136
To register a custom provider, create a file in your JAR:
137
138
**File**: `src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider`
139
140
**Content**:
141
```
142
com.example.CustomDatabaseContainerProvider
143
com.example.AnotherDatabaseContainerProvider
144
```
145
146
### Custom Provider Implementation
147
148
Example of a complete custom provider:
149
150
```java
151
package com.example;
152
153
import org.testcontainers.containers.JdbcDatabaseContainer;
154
import org.testcontainers.containers.JdbcDatabaseContainerProvider;
155
import org.testcontainers.jdbc.ConnectionUrl;
156
157
public class CustomDatabaseContainerProvider extends JdbcDatabaseContainerProvider {
158
159
@Override
160
public boolean supports(String databaseType) {
161
return "customdb".equals(databaseType);
162
}
163
164
@Override
165
public JdbcDatabaseContainer newInstance() {
166
return newInstance("1.0"); // Stable default version
167
}
168
169
@Override
170
public JdbcDatabaseContainer newInstance(String tag) {
171
return new CustomDatabaseContainer("customdb:" + tag);
172
}
173
174
@Override
175
public JdbcDatabaseContainer newInstance(ConnectionUrl url) {
176
final JdbcDatabaseContainer container;
177
178
if (url.getImageTag().isPresent()) {
179
container = newInstance(url.getImageTag().get());
180
} else {
181
container = newInstance();
182
}
183
184
// Configure reusability from URL
185
container.withReuse(url.isReusable());
186
187
// Extract database name, username, password from URL parameters
188
String dbName = url.getDatabaseName().orElse("defaultdb");
189
String user = url.getQueryParameters().getOrDefault("user", "test");
190
String password = url.getQueryParameters().getOrDefault("password", "test");
191
192
return container
193
.withDatabaseName(dbName)
194
.withUsername(user)
195
.withPassword(password);
196
}
197
}
198
```
199
200
## Built-in Providers
201
202
Testcontainers includes providers for common databases:
203
204
### MySQL Provider
205
- **Database Type**: `mysql`
206
- **Default Image**: `mysql:8.0`
207
- **Parameters**: `user`, `password`
208
209
### PostgreSQL Provider
210
- **Database Type**: `postgresql`
211
- **Default Image**: `postgres:13`
212
- **Parameters**: `user`, `password`
213
214
### Oracle Provider
215
- **Database Type**: `oracle`
216
- **Special URL Format**: Supports Oracle thin client URL format
217
- **Parameters**: `user`, `password`
218
219
### SQL Server Provider
220
- **Database Type**: `sqlserver`
221
- **Default Image**: `mcr.microsoft.com/mssql/server`
222
- **Parameters**: `user`, `password`
223
224
## Provider Selection Process
225
226
When the JDBC driver encounters a `jdbc:tc:` URL:
227
228
1. **URL Parsing**: Extract database type from URL (e.g., `mysql` from `jdbc:tc:mysql:8.0://...`)
229
2. **Provider Discovery**: Use ServiceLoader to find all available providers
230
3. **Provider Matching**: Call `supports(databaseType)` on each provider until match found
231
4. **Container Creation**: Use matching provider to create container instance
232
5. **Configuration**: Apply URL parameters and container-specific settings
233
6. **Error Handling**: If no provider supports the database type, throw `UnsupportedOperationException`
234
235
## Provider Best Practices
236
237
### Stable Default Versions
238
Always override `newInstance()` to provide a stable default version instead of "latest":
239
240
```java
241
@Override
242
public JdbcDatabaseContainer newInstance() {
243
// Good: stable version
244
return newInstance("13.7");
245
246
// Avoid: unpredictable "latest"
247
// return newInstance("latest");
248
}
249
```
250
251
### Parameter Handling
252
Use the helper method `newInstanceFromConnectionUrl()` for consistent parameter extraction:
253
254
```java
255
@Override
256
public JdbcDatabaseContainer newInstance(ConnectionUrl url) {
257
return newInstanceFromConnectionUrl(url, "user", "password");
258
}
259
```
260
261
### Image Tag Validation
262
Validate image tags when possible to prevent runtime errors:
263
264
```java
265
@Override
266
public JdbcDatabaseContainer newInstance(String tag) {
267
if (tag == null || tag.trim().isEmpty()) {
268
throw new IllegalArgumentException("Image tag cannot be null or empty");
269
}
270
return new MySQLContainer<>("mysql:" + tag);
271
}
272
```
273
274
### Resource Management
275
Ensure containers are properly configured for resource cleanup:
276
277
```java
278
@Override
279
public JdbcDatabaseContainer newInstance(String tag) {
280
return new MySQLContainer<>("mysql:" + tag)
281
.withReuse(false) // Explicit cleanup unless URL specifies reuse
282
.withStartupTimeout(Duration.ofMinutes(2));
283
}
284
```