0
# Embedded Server Support
1
2
Embedded LDAP server containers for testing and development environments with support for Apache Directory Server and UnboundID implementations.
3
4
## Capabilities
5
6
### EmbeddedLdapServerContainer
7
8
Base interface for embedded LDAP server containers.
9
10
```java { .api }
11
/**
12
* Base interface for embedded LDAP server containers providing common lifecycle operations
13
*/
14
public interface EmbeddedLdapServerContainer {
15
/**
16
* Gets the port number the embedded server is listening on
17
* @return the LDAP port number
18
*/
19
int getPort();
20
21
/**
22
* Sets the port number for the embedded LDAP server
23
* @param port the port number to use
24
*/
25
void setPort(int port);
26
27
/**
28
* Starts the embedded LDAP server
29
* @throws Exception if server startup fails
30
*/
31
void start() throws Exception;
32
33
/**
34
* Stops the embedded LDAP server
35
* @throws Exception if server shutdown fails
36
*/
37
void stop() throws Exception;
38
39
/**
40
* Checks if the embedded server is currently running
41
* @return true if the server is running
42
*/
43
boolean isRunning();
44
}
45
```
46
47
### ApacheDSContainer
48
49
Embedded Apache Directory Server container for testing and development with Spring Security LDAP.
50
51
```java { .api }
52
/**
53
* Embedded Apache Directory Server container providing LDAP server functionality
54
* for testing and development environments
55
*/
56
public class ApacheDSContainer implements InitializingBean, DisposableBean, Lifecycle {
57
/**
58
* Creates an Apache DS container with root DN and LDIF data
59
* @param root the root DN for the directory (e.g., "dc=springframework,dc=org")
60
* @param ldifs comma-separated list of LDIF file paths to load
61
*/
62
public ApacheDSContainer(String root, String ldifs);
63
64
/**
65
* Starts the embedded Apache Directory Server
66
* @throws Exception if server startup fails
67
*/
68
public void start() throws Exception;
69
70
/**
71
* Stops the embedded Apache Directory Server
72
* @throws Exception if server shutdown fails
73
*/
74
public void stop() throws Exception;
75
76
/**
77
* Checks if the server is currently running
78
* @return true if server is running
79
*/
80
public boolean isRunning();
81
82
/**
83
* Gets the port number the server is listening on
84
* @return the LDAP port number
85
*/
86
public int getPort();
87
88
/**
89
* Sets the port number for the LDAP server
90
* @param port the port number (default: random available port)
91
*/
92
public void setPort(int port);
93
94
/**
95
* Sets additional Apache DS configuration
96
* @param ldifFile path to LDIF file containing additional configuration
97
*/
98
public void setLdifFile(String ldifFile);
99
100
/**
101
* Sets the working directory for the server
102
* @param workingDirectory the working directory path
103
*/
104
public void setWorkingDirectory(String workingDirectory);
105
106
/**
107
* Performs initialization after properties are set
108
*/
109
public void afterPropertiesSet() throws Exception;
110
111
/**
112
* Cleanup resources when bean is destroyed
113
*/
114
public void destroy() throws Exception;
115
}
116
```
117
118
**Usage Examples:**
119
120
```java
121
// Basic Apache DS container setup
122
ApacheDSContainer container = new ApacheDSContainer("dc=springframework,dc=org",
123
"classpath:test-data.ldif");
124
container.setPort(33389);
125
container.start();
126
127
// With custom working directory
128
container.setWorkingDirectory("target/apacheds-work");
129
130
// Lifecycle management
131
container.afterPropertiesSet();
132
// ... use the server for testing
133
container.destroy();
134
```
135
136
### UnboundIdContainer
137
138
Embedded UnboundID LDAP server container offering high performance for testing scenarios.
139
140
```java { .api }
141
/**
142
* Embedded UnboundID LDAP server container providing high-performance LDAP server
143
* functionality for testing and development environments
144
*/
145
public class UnboundIdContainer implements InitializingBean, DisposableBean, Lifecycle {
146
/**
147
* Creates an UnboundID container with root DN and LDIF data
148
* @param root the root DN for the directory
149
* @param ldif path to LDIF file containing initial data
150
*/
151
public UnboundIdContainer(String root, String ldif);
152
153
/**
154
* Starts the embedded UnboundID LDAP server
155
* @throws Exception if server startup fails
156
*/
157
public void start() throws Exception;
158
159
/**
160
* Stops the embedded UnboundID LDAP server
161
* @throws Exception if server shutdown fails
162
*/
163
public void stop() throws Exception;
164
165
/**
166
* Checks if the server is currently running
167
* @return true if server is running
168
*/
169
public boolean isRunning();
170
171
/**
172
* Gets the port number the server is listening on
173
* @return the LDAP port number
174
*/
175
public int getPort();
176
177
/**
178
* Sets the port number for the LDAP server
179
* @param port the port number (default: random available port)
180
*/
181
public void setPort(int port);
182
183
/**
184
* Sets the manager distinguished name for administrative operations
185
* @param managerDn the manager DN (e.g., "cn=Directory Manager")
186
*/
187
public void setManagerDn(String managerDn);
188
189
/**
190
* Sets the manager password for administrative operations
191
* @param managerPassword the manager password
192
*/
193
public void setManagerPassword(String managerPassword);
194
195
/**
196
* Performs initialization after properties are set
197
*/
198
public void afterPropertiesSet() throws Exception;
199
200
/**
201
* Cleanup resources when bean is destroyed
202
*/
203
public void destroy() throws Exception;
204
}
205
```
206
207
**Usage Examples:**
208
209
```java
210
// Basic UnboundID container setup
211
UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org",
212
"classpath:test-users.ldif");
213
container.setPort(33389);
214
container.setManagerDn("cn=Directory Manager");
215
container.setManagerPassword("password");
216
container.start();
217
218
// Lifecycle management in tests
219
@BeforeEach
220
void setUp() throws Exception {
221
container.afterPropertiesSet();
222
container.start();
223
}
224
225
@AfterEach
226
void tearDown() throws Exception {
227
container.stop();
228
container.destroy();
229
}
230
```
231
232
## Test Configuration Examples
233
234
### Spring Test Configuration with Embedded Server
235
236
```java
237
@TestConfiguration
238
@Profile("embedded-ldap")
239
public class EmbeddedLdapConfig {
240
241
@Bean
242
@Primary
243
public UnboundIdContainer embeddedLdapServer() {
244
UnboundIdContainer container = new UnboundIdContainer(
245
"dc=springframework,dc=org",
246
"classpath:test-ldap-data.ldif"
247
);
248
container.setPort(0); // Use random available port
249
container.setManagerDn("cn=Directory Manager");
250
container.setManagerPassword("password");
251
return container;
252
}
253
254
@Bean
255
@Primary
256
@DependsOn("embeddedLdapServer")
257
public DefaultSpringSecurityContextSource embeddedContextSource() {
258
int port = embeddedLdapServer().getPort();
259
DefaultSpringSecurityContextSource contextSource =
260
new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
261
contextSource.setUserDn("cn=Directory Manager");
262
contextSource.setPassword("password");
263
return contextSource;
264
}
265
}
266
```
267
268
### JUnit 5 Test with Embedded LDAP
269
270
```java
271
@SpringBootTest
272
@ActiveProfiles("embedded-ldap")
273
class LdapAuthenticationTest {
274
275
@Autowired
276
private UnboundIdContainer embeddedLdapServer;
277
278
@Autowired
279
private LdapAuthenticationProvider authenticationProvider;
280
281
@Test
282
void shouldAuthenticateValidUser() {
283
// Given
284
UsernamePasswordAuthenticationToken token =
285
new UsernamePasswordAuthenticationToken("john", "password");
286
287
// When
288
Authentication result = authenticationProvider.authenticate(token);
289
290
// Then
291
assertThat(result.isAuthenticated()).isTrue();
292
assertThat(result.getName()).isEqualTo("john");
293
assertThat(result.getAuthorities()).hasSize(2);
294
}
295
}
296
```
297
298
### Integration Test Setup
299
300
```java
301
@ExtendWith(SpringExtension.class)
302
@ContextConfiguration(classes = {EmbeddedLdapConfig.class, LdapSecurityConfig.class})
303
class LdapIntegrationTest {
304
305
@Autowired
306
private ApacheDSContainer ldapServer;
307
308
@Autowired
309
private LdapUserDetailsService userDetailsService;
310
311
@BeforeEach
312
void startLdapServer() throws Exception {
313
if (!ldapServer.isRunning()) {
314
ldapServer.start();
315
}
316
}
317
318
@AfterEach
319
void stopLdapServer() throws Exception {
320
if (ldapServer.isRunning()) {
321
ldapServer.stop();
322
}
323
}
324
325
@Test
326
void shouldLoadUserDetails() {
327
UserDetails user = userDetailsService.loadUserByUsername("alice");
328
329
assertThat(user).isNotNull();
330
assertThat(user.getUsername()).isEqualTo("alice");
331
assertThat(user.getAuthorities()).isNotEmpty();
332
}
333
}
334
```
335
336
## LDIF Data Examples
337
338
### Sample LDIF for Testing
339
340
```ldif
341
# test-ldap-data.ldif
342
dn: dc=springframework,dc=org
343
objectClass: top
344
objectClass: domain
345
objectClass: extensibleObject
346
dc: springframework
347
348
dn: ou=people,dc=springframework,dc=org
349
objectClass: top
350
objectClass: organizationalUnit
351
ou: people
352
353
dn: ou=groups,dc=springframework,dc=org
354
objectClass: top
355
objectClass: organizationalUnit
356
ou: groups
357
358
# Users
359
dn: uid=john,ou=people,dc=springframework,dc=org
360
objectClass: top
361
objectClass: person
362
objectClass: organizationalPerson
363
objectClass: inetOrgPerson
364
uid: john
365
cn: John Smith
366
sn: Smith
367
mail: john@springframework.org
368
userPassword: password
369
370
dn: uid=alice,ou=people,dc=springframework,dc=org
371
objectClass: top
372
objectClass: person
373
objectClass: organizationalPerson
374
objectClass: inetOrgPerson
375
uid: alice
376
cn: Alice Johnson
377
sn: Johnson
378
mail: alice@springframework.org
379
userPassword: secret
380
381
# Groups
382
dn: cn=users,ou=groups,dc=springframework,dc=org
383
objectClass: top
384
objectClass: groupOfNames
385
cn: users
386
member: uid=john,ou=people,dc=springframework,dc=org
387
member: uid=alice,ou=people,dc=springframework,dc=org
388
389
dn: cn=admins,ou=groups,dc=springframework,dc=org
390
objectClass: top
391
objectClass: groupOfNames
392
cn: admins
393
member: uid=alice,ou=people,dc=springframework,dc=org
394
```
395
396
### Complex Test Data Structure
397
398
```java
399
@TestConfiguration
400
public class LdapTestDataConfig {
401
402
@Bean
403
public UnboundIdContainer ldapServerWithComplexData() {
404
UnboundIdContainer container = new UnboundIdContainer(
405
"dc=example,dc=com",
406
"classpath:complex-ldap-structure.ldif"
407
);
408
409
// Configure for complex scenarios
410
container.setPort(0);
411
container.setManagerDn("cn=Directory Manager");
412
container.setManagerPassword("admin123");
413
414
return container;
415
}
416
417
// Bean that loads additional test data after server start
418
@Bean
419
@DependsOn("ldapServerWithComplexData")
420
public LdapTestDataLoader testDataLoader() {
421
return new LdapTestDataLoader(embeddedContextSource());
422
}
423
}
424
425
@Component
426
public class LdapTestDataLoader {
427
428
private final SpringSecurityLdapTemplate ldapTemplate;
429
430
public LdapTestDataLoader(ContextSource contextSource) {
431
this.ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
432
}
433
434
@PostConstruct
435
public void loadAdditionalTestData() {
436
// Add test data programmatically
437
createTestOrganizationalUnits();
438
createTestUsers();
439
createTestGroups();
440
}
441
442
private void createTestOrganizationalUnits() {
443
// Create additional OUs for testing
444
DirContextAdapter context = new DirContextAdapter("ou=departments,dc=example,dc=com");
445
context.setAttributeValues("objectClass", new String[]{"top", "organizationalUnit"});
446
context.setAttributeValue("ou", "departments");
447
ldapTemplate.bind(context);
448
}
449
450
// Additional helper methods for test data creation...
451
}
452
```
453
454
### Performance Testing Configuration
455
456
```java
457
@TestConfiguration
458
@Profile("performance-test")
459
public class PerformanceLdapConfig {
460
461
@Bean
462
public UnboundIdContainer performanceTestLdapServer() {
463
UnboundIdContainer container = new UnboundIdContainer(
464
"dc=perf,dc=test",
465
"classpath:large-dataset.ldif"
466
);
467
468
// Optimize for performance testing
469
container.setPort(33389); // Fixed port for consistency
470
container.setManagerDn("cn=Directory Manager");
471
container.setManagerPassword("admin");
472
473
return container;
474
}
475
476
@Bean
477
@Primary
478
public DefaultSpringSecurityContextSource performanceContextSource() {
479
DefaultSpringSecurityContextSource contextSource =
480
new DefaultSpringSecurityContextSource("ldap://localhost:33389/dc=perf,dc=test");
481
482
// Connection pool settings for performance
483
contextSource.setPooled(true);
484
485
Map<String, Object> baseEnv = new HashMap<>();
486
baseEnv.put("com.sun.jndi.ldap.connect.pool.maxsize", "20");
487
baseEnv.put("com.sun.jndi.ldap.connect.pool.prefsize", "10");
488
baseEnv.put("com.sun.jndi.ldap.connect.pool.timeout", "300000");
489
contextSource.setBaseEnvironmentProperties(baseEnv);
490
491
return contextSource;
492
}
493
}
494
```
495
496
### Docker-based Testing Alternative
497
498
```java
499
@Testcontainers
500
@SpringBootTest
501
class DockerLdapTest {
502
503
@Container
504
static final GenericContainer<?> ldapContainer = new GenericContainer<>("osixia/openldap:1.5.0")
505
.withExposedPorts(389)
506
.withEnv("LDAP_ORGANISATION", "Test Org")
507
.withEnv("LDAP_DOMAIN", "test.org")
508
.withEnv("LDAP_ADMIN_PASSWORD", "admin")
509
.withClasspathResourceMapping("test-data.ldif", "/container/service/slapd/assets/config/bootstrap/ldif/50-bootstrap.ldif", BindMode.READ_ONLY);
510
511
@DynamicPropertySource
512
static void configureProperties(DynamicPropertyRegistry registry) {
513
registry.add("ldap.url", () -> "ldap://localhost:" + ldapContainer.getMappedPort(389));
514
registry.add("ldap.base", () -> "dc=test,dc=org");
515
registry.add("ldap.username", () -> "cn=admin,dc=test,dc=org");
516
registry.add("ldap.password", () -> "admin");
517
}
518
519
@Test
520
void shouldConnectToDockerLdap() {
521
// Test LDAP connectivity and operations
522
assertThat(ldapContainer.isRunning()).isTrue();
523
}
524
}
525
```