Skip to content

Commit 538e0e0

Browse files
committed
EmbeddedLdapServer builder
Closes #938 Signed-off-by: emanueltrandafir1993 <[email protected]>
1 parent 7af94ea commit 538e0e0

File tree

5 files changed

+273
-49
lines changed

5 files changed

+273
-49
lines changed

test-support/src/main/java/org/springframework/ldap/test/unboundid/EmbeddedLdapServer.java

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,24 @@ public EmbeddedLdapServer(InMemoryDirectoryServer directoryServer) {
4545
this.directoryServer = directoryServer;
4646
}
4747

48+
/**
49+
* Creates a new {@link EmbeddedLdapServerBuilder}.
50+
*
51+
* @since 3.3
52+
*/
53+
public static EmbeddedLdapServerBuilder builder() {
54+
return new EmbeddedLdapServerBuilder();
55+
}
56+
4857
/**
4958
* Creates and starts new embedded LDAP server.
5059
*/
5160
public static EmbeddedLdapServer newEmbeddedServer(String defaultPartitionName, String defaultPartitionSuffix,
5261
int port) throws Exception {
53-
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(defaultPartitionSuffix);
54-
config.addAdditionalBindCredentials("uid=admin,ou=system", "secret");
55-
56-
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", port));
62+
InMemoryDirectoryServerConfig config = inMemoryDirectoryServerConfig(defaultPartitionSuffix, port);
63+
Entry entry = ldapEntry(defaultPartitionName, defaultPartitionSuffix);
5764

58-
config.setEnforceSingleStructuralObjectClass(false);
59-
config.setEnforceAttributeSyntaxCompliance(true);
60-
61-
Entry entry = new Entry(new DN(defaultPartitionSuffix));
62-
entry.addAttribute("objectClass", "top", "domain", "extensibleObject");
63-
entry.addAttribute("dc", defaultPartitionName);
64-
65-
InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
66-
directoryServer.add(entry);
65+
InMemoryDirectoryServer directoryServer = inMemoryDirectoryServer(config, entry);
6766
directoryServer.startListening();
6867
return new EmbeddedLdapServer(directoryServer);
6968
}
@@ -102,4 +101,28 @@ public void shutdown() {
102101
this.directoryServer.shutDown(true);
103102
}
104103

104+
static InMemoryDirectoryServerConfig inMemoryDirectoryServerConfig(String partitionSuffix, int port)
105+
throws LDAPException {
106+
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(partitionSuffix);
107+
config.addAdditionalBindCredentials("uid=admin,ou=system", "secret");
108+
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", port));
109+
config.setEnforceSingleStructuralObjectClass(false);
110+
config.setEnforceAttributeSyntaxCompliance(true);
111+
return config;
112+
}
113+
114+
static Entry ldapEntry(String defaultPartitionName, String defaultPartitionSuffix) throws LDAPException {
115+
Entry entry = new Entry(new DN(defaultPartitionSuffix));
116+
entry.addAttribute("objectClass", "top", "domain", "extensibleObject");
117+
entry.addAttribute("dc", defaultPartitionName);
118+
return entry;
119+
}
120+
121+
static InMemoryDirectoryServer inMemoryDirectoryServer(InMemoryDirectoryServerConfig config, Entry entry)
122+
throws LDAPException {
123+
InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
124+
directoryServer.add(entry);
125+
return directoryServer;
126+
}
127+
105128
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2005-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ldap.test.unboundid;
18+
19+
import java.util.function.Consumer;
20+
21+
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
22+
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
23+
import com.unboundid.ldap.sdk.Entry;
24+
25+
/**
26+
* Helper class for embedded Unboundid ldap server.
27+
*
28+
* @author Emanuel Trandafir
29+
* @since 3.3
30+
*/
31+
public class EmbeddedLdapServerBuilder {
32+
33+
private int port = 0;
34+
35+
private Consumer<InMemoryDirectoryServerConfig> configurationCustomizer = (__) -> {
36+
};
37+
38+
private String partitionSuffix = "dc=jayway,dc=se";
39+
40+
private String partitionName = "jayway";
41+
42+
EmbeddedLdapServerBuilder() {
43+
}
44+
45+
public EmbeddedLdapServerBuilder withPort(int port) {
46+
this.port = port;
47+
return this;
48+
}
49+
50+
public EmbeddedLdapServerBuilder withConfigurationCustomizer(
51+
Consumer<InMemoryDirectoryServerConfig> configurationCustomizer) {
52+
this.configurationCustomizer = configurationCustomizer;
53+
return this;
54+
}
55+
56+
public EmbeddedLdapServerBuilder withPartitionSuffix(String defaultPartitionSuffix) {
57+
this.partitionSuffix = defaultPartitionSuffix;
58+
return this;
59+
}
60+
61+
public EmbeddedLdapServerBuilder withPartitionName(String defaultPartitionName) {
62+
this.partitionName = defaultPartitionName;
63+
return this;
64+
}
65+
66+
/**
67+
* Builds and returns a {@link EmbeddedLdapServer}.
68+
* <p>
69+
* In order to start the server, you should call {@link EmbeddedLdapServer#start()}.
70+
* @return a new {@link EmbeddedLdapServer}.
71+
*/
72+
public EmbeddedLdapServer build() {
73+
try {
74+
InMemoryDirectoryServerConfig config = EmbeddedLdapServer
75+
.inMemoryDirectoryServerConfig(this.partitionSuffix, this.port);
76+
this.configurationCustomizer.accept(config);
77+
78+
Entry entry = EmbeddedLdapServer.ldapEntry(this.partitionName, this.partitionSuffix);
79+
InMemoryDirectoryServer directoryServer = EmbeddedLdapServer.inMemoryDirectoryServer(config, entry);
80+
return new EmbeddedLdapServer(directoryServer);
81+
}
82+
catch (Exception ex) {
83+
throw new RuntimeException(ex);
84+
}
85+
}
86+
87+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2005-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ldap.test.unboundid;
18+
19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
23+
import org.junit.BeforeClass;
24+
import org.junit.Test;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
public class EmbeddedLdapServerBuilderTests {
29+
30+
private static String tempLogFile;
31+
32+
@BeforeClass
33+
public static void before() throws IOException {
34+
tempLogFile = Files.createTempFile("ldap-log-", ".txt").toAbsolutePath().toString();
35+
}
36+
37+
@Test
38+
public void testServerStartup_withCustomConfig() {
39+
EmbeddedLdapServerBuilder serverBuilder = EmbeddedLdapServer.builder()
40+
.withPort(1234)
41+
.withConfigurationCustomizer((config) -> config.setCodeLogDetails(tempLogFile, true));
42+
43+
try (EmbeddedLdapServer server = serverBuilder.build()) {
44+
server.start();
45+
46+
// ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
47+
// "/applicationContext-testContextSource.xml");
48+
// LdapTemplate ldapTemplate = ctx.getBean(LdapTemplate.class);
49+
// assertThat(ldapTemplate).isNotNull();
50+
//
51+
// ldapTemplate.search(LdapQueryBuilder.query().where("objectclass").is("person"),
52+
// new AttributesMapper<>() {
53+
// public String mapFromAttributes(Attributes attrs) throws NamingException {
54+
// return (String) attrs.get("cn").get();
55+
// }
56+
// });
57+
58+
assertThat(Path.of(tempLogFile)).isNotEmptyFile();
59+
}
60+
}
61+
62+
@Test
63+
public void shouldBuildButNotStartTheServer() {
64+
int port = SocketUtils.getFreePort();
65+
66+
EmbeddedLdapServer server = EmbeddedLdapServer.builder().withPort(port).build();
67+
68+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
69+
}
70+
71+
@Test
72+
public void shouldBuildTheServerWithCustomPort() {
73+
int port = SocketUtils.getFreePort();
74+
75+
EmbeddedLdapServerBuilder serverBuilder = EmbeddedLdapServer.builder()
76+
.withPartitionName("test")
77+
.withPartitionSuffix("dc=test,dc=se")
78+
.withPort(port);
79+
80+
try (EmbeddedLdapServer server = serverBuilder.build()) {
81+
server.start();
82+
assertThat(SocketUtils.isPortOpen(port)).isTrue();
83+
}
84+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
85+
}
86+
87+
}

test-support/src/test/java/org/springframework/ldap/test/unboundid/EmbeddedLdapServerTests.java

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
package org.springframework.ldap.test.unboundid;
1818

19-
import java.io.IOException;
20-
import java.net.ServerSocket;
21-
import java.net.Socket;
22-
2319
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
2420
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
2521
import com.unboundid.ldap.listener.InMemoryListenerConfig;
@@ -32,84 +28,69 @@ public class EmbeddedLdapServerTests {
3228

3329
@Test
3430
public void shouldStartAndCloseServer() throws Exception {
35-
int port = getFreePort();
36-
assertThat(isPortOpen(port)).isFalse();
31+
int port = SocketUtils.getFreePort();
32+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
3733

3834
EmbeddedLdapServer server = EmbeddedLdapServer.newEmbeddedServer("jayway", "dc=jayway,dc=se", port);
39-
assertThat(isPortOpen(port)).isTrue();
35+
assertThat(SocketUtils.isPortOpen(port)).isTrue();
4036

4137
server.close();
42-
assertThat(isPortOpen(port)).isFalse();
38+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
4339
}
4440

4541
@Test
4642
public void shouldStartAndAutoCloseServer() throws Exception {
47-
int port = getFreePort();
48-
assertThat(isPortOpen(port)).isFalse();
43+
int port = SocketUtils.getFreePort();
44+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
4945

5046
try (EmbeddedLdapServer ignored = EmbeddedLdapServer.newEmbeddedServer("jayway", "dc=jayway,dc=se", port)) {
51-
assertThat(isPortOpen(port)).isTrue();
47+
assertThat(SocketUtils.isPortOpen(port)).isTrue();
5248
}
53-
assertThat(isPortOpen(port)).isFalse();
49+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
5450
}
5551

5652
@Test
5753
public void shouldStartAndCloseServerViaLdapTestUtils() throws Exception {
58-
int port = getFreePort();
59-
assertThat(isPortOpen(port)).isFalse();
54+
int port = SocketUtils.getFreePort();
55+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
6056

6157
LdapTestUtils.startEmbeddedServer(port, "dc=jayway,dc=se", "jayway");
62-
assertThat(isPortOpen(port)).isTrue();
58+
assertThat(SocketUtils.isPortOpen(port)).isTrue();
6359

6460
LdapTestUtils.shutdownEmbeddedServer();
65-
assertThat(isPortOpen(port)).isFalse();
61+
assertThat(SocketUtils.isPortOpen(port)).isFalse();
6662
}
6763

6864
@Test
6965
public void startWhenNewEmbeddedServerThenException() throws Exception {
70-
int port = getFreePort();
66+
int port = SocketUtils.getFreePort();
7167
EmbeddedLdapServer server = EmbeddedLdapServer.newEmbeddedServer("jayway", "dc=jayway,dc=se", port);
7268
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(server::start);
7369
}
7470

7571
@Test
7672
public void startWhenUnstartedThenWorks() throws Exception {
77-
int port = getFreePort();
73+
int port = SocketUtils.getFreePort();
7874
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=jayway,dc=se");
7975
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", port));
8076
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
8177
try (EmbeddedLdapServer server = new EmbeddedLdapServer(ds)) {
8278
server.start();
83-
assertThat(isPortOpen(port)).isTrue();
79+
assertThat(SocketUtils.isPortOpen(port)).isTrue();
8480
}
8581
}
8682

8783
@Test
8884
public void startWhenAlreadyStartedThenFails() throws Exception {
89-
int port = getFreePort();
85+
int port = SocketUtils.getFreePort();
9086
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=jayway,dc=se");
9187
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", port));
9288
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
9389
try (EmbeddedLdapServer server = new EmbeddedLdapServer(ds)) {
9490
server.start();
95-
assertThat(isPortOpen(port)).isTrue();
91+
assertThat(SocketUtils.isPortOpen(port)).isTrue();
9692
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(server::start);
9793
}
9894
}
9995

100-
static boolean isPortOpen(int port) {
101-
try (Socket ignored = new Socket("localhost", port)) {
102-
return true;
103-
}
104-
catch (IOException ex) {
105-
return false;
106-
}
107-
}
108-
109-
static int getFreePort() throws IOException {
110-
try (ServerSocket serverSocket = new ServerSocket(0)) {
111-
return serverSocket.getLocalPort();
112-
}
113-
}
114-
11596
}

0 commit comments

Comments
 (0)