From 7a9a69bda346d0ca92c6227b5a38ec709c602f6f Mon Sep 17 00:00:00 2001 From: Krzysztof Majewski Date: Fri, 26 Nov 2021 18:06:20 +0100 Subject: [PATCH] BAEL-5076 Unix domain socket in Java 16 (#11493) * BAEL-5076 Unix domain socket in Java 16 * BAEL-5076 Unix domain socket in Java 16 * BAEL-5076 Unix domain socket in Java 16 * BAEL-5076 Unix domain socket in Java 16 Co-authored-by: krzysztof --- .../core-java-networking-3/pom.xml | 10 +++ .../socket/UnixDomainSocketClient.java | 49 +++++++++++ .../socket/UnixDomainSocketServer.java | 57 +++++++++++++ .../UnixDomainSocketClientUnitTest.java | 82 +++++++++++++++++++ .../UnixDomainSocketServerUnitTest.java | 50 +++++++++++ core-java-modules/pom.xml | 1 - pom.xml | 2 + 7 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketClient.java create mode 100644 core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketServer.java create mode 100644 core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketClientUnitTest.java create mode 100644 core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketServerUnitTest.java diff --git a/core-java-modules/core-java-networking-3/pom.xml b/core-java-modules/core-java-networking-3/pom.xml index ee822e57cb..297d665544 100644 --- a/core-java-modules/core-java-networking-3/pom.xml +++ b/core-java-modules/core-java-networking-3/pom.xml @@ -64,6 +64,16 @@ + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketClient.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketClient.java new file mode 100644 index 0000000000..2a8ae05628 --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketClient.java @@ -0,0 +1,49 @@ +package com.baeldung.socket; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; + +class UnixDomainSocketClient { + + public static void main(String[] args) throws Exception { + new UnixDomainSocketClient().runClient(); + } + + void runClient() throws IOException { + Path socketPath = Path.of(System.getProperty("user.home")) + .resolve("baeldung.socket"); + UnixDomainSocketAddress socketAddress = getAddress(socketPath); + + SocketChannel channel = openSocketChannel(socketAddress); + + String message = "Hello from Baeldung Unix domain socket article"; + writeMessage(channel, message); + } + + UnixDomainSocketAddress getAddress(Path socketPath) { + return UnixDomainSocketAddress.of(socketPath); + } + + SocketChannel openSocketChannel(UnixDomainSocketAddress socketAddress) throws IOException { + SocketChannel channel = SocketChannel + .open(StandardProtocolFamily.UNIX); + channel.connect(socketAddress); + return channel; + } + + void writeMessage(SocketChannel socketChannel, String message) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + buffer.clear(); + buffer.put(message.getBytes()); + buffer.flip(); + + while (buffer.hasRemaining()) { + socketChannel.write(buffer); + } + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketServer.java b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketServer.java new file mode 100644 index 0000000000..c3093ae00c --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/main/java/com/baeldung/socket/UnixDomainSocketServer.java @@ -0,0 +1,57 @@ +package com.baeldung.socket; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; + +class UnixDomainSocketServer { + + public static void main(String[] args) throws IOException, InterruptedException { + new UnixDomainSocketServer().runServer(); + } + + void runServer() throws IOException, InterruptedException { + Path socketPath = Path.of(System.getProperty("user.home")) + .resolve("baeldung.socket"); + Files.deleteIfExists(socketPath); + UnixDomainSocketAddress socketAddress = getAddress(socketPath); + + ServerSocketChannel serverChannel = createServerSocketChannel(socketAddress); + + SocketChannel channel = serverChannel.accept(); + + while (true) { + readSocketMessage(channel) + .ifPresent(message -> System.out.printf("[Client message] %s%n", message)); + Thread.sleep(100); + } + } + + UnixDomainSocketAddress getAddress(Path socketPath) { + return UnixDomainSocketAddress.of(socketPath); + } + + ServerSocketChannel createServerSocketChannel(UnixDomainSocketAddress socketAddress) throws IOException { + ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + serverChannel.bind(socketAddress); + return serverChannel; + } + + Optional readSocketMessage(SocketChannel channel) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + int bytesRead = channel.read(buffer); + if (bytesRead < 0) return Optional.empty(); + byte[] bytes = new byte[bytesRead]; + buffer.flip(); + buffer.get(bytes); + String message = new String(bytes); + return Optional.of(message); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketClientUnitTest.java b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketClientUnitTest.java new file mode 100644 index 0000000000..8cd2ee94d4 --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketClientUnitTest.java @@ -0,0 +1,82 @@ +package com.baeldung.socket; + +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +import java.io.File; +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; +import java.util.UUID; + +import static java.nio.file.Files.deleteIfExists; +import static org.assertj.core.util.Files.newTemporaryFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class UnixDomainSocketClientUnitTest { + + @Test + public void givenSocketPath_shouldCreateUnixDomainSocketAddress() { + // given + File tempFile = newTemporaryFile(); + Path socketPath = tempFile.toPath(); + + // when + UnixDomainSocketAddress address = new UnixDomainSocketClient().getAddress(socketPath); + + // then + assertEquals(address.getPath(), socketPath); + + // cleanup + tempFile.delete(); + } + + @Test + public void givenUnixDomainSocketAddress_shouldOpenSocketChannel() throws IOException { + // given + File tempFile = newTemporaryFile(); + Path socketPath = tempFile.toPath(); + deleteIfExists(socketPath); + UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath); + + // bind address as a unix domain socket + ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + serverChannel.bind(address); + + // when + SocketChannel socketChannel = new UnixDomainSocketClient().openSocketChannel(address); + + // then + assertTrue(socketChannel.isOpen()); + assertEquals(socketChannel.getRemoteAddress(), address); + + // cleanup + tempFile.delete(); + } + + @Test + public void givenSocketChannelAndMessage_shouldWriteMessage() throws IOException { + // given + SocketChannel socketChannel = Mockito.mock(SocketChannel.class); + String message = UUID.randomUUID().toString(); + Mockito.when(socketChannel.write(Mockito.any(ByteBuffer.class))) + .thenAnswer( + (Answer) invocationOnMock -> { + ((ByteBuffer) invocationOnMock.getArguments()[0]).position(message.getBytes().length); + return -1; + } + ); + + // when + new UnixDomainSocketClient().writeMessage(socketChannel, message); + + // then + Mockito.verify(socketChannel, Mockito.times(1)).write(Mockito.any(ByteBuffer.class)); + } +} diff --git a/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketServerUnitTest.java b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketServerUnitTest.java new file mode 100644 index 0000000000..1d0a68ac2f --- /dev/null +++ b/core-java-modules/core-java-networking-3/src/test/java/com/baeldung/socket/UnixDomainSocketServerUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.socket; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.file.Path; + +import static java.nio.file.Files.deleteIfExists; +import static org.assertj.core.util.Files.newTemporaryFile; +import static org.junit.Assert.assertEquals; + +public class UnixDomainSocketServerUnitTest { + + @Test + public void givenSocketPath_shouldCreateUnixDomainSocketAddress() { + // given + File tempFile = newTemporaryFile(); + Path socketPath = tempFile.toPath(); + + // when + UnixDomainSocketAddress address = new UnixDomainSocketServer().getAddress(socketPath); + + // then + assertEquals(address.getPath(), socketPath); + + // cleanup + tempFile.delete(); + } + + @Test + public void givenUnixDomainSocketAddress_shouldCreateServerSocketChannel() throws IOException { + // given + File tempFile = newTemporaryFile(); + Path socketPath = tempFile.toPath(); + deleteIfExists(socketPath); + UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath); + + // when + ServerSocketChannel serverSocketChannel = new UnixDomainSocketServer().createServerSocketChannel(address); + + // then + assertEquals(serverSocketChannel.getLocalAddress(), address); + + // cleanup + tempFile.delete(); + } +} diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml index a57cd54191..d9da5a845b 100644 --- a/core-java-modules/pom.xml +++ b/core-java-modules/pom.xml @@ -90,7 +90,6 @@ core-java-lang-syntax-2 core-java-networking core-java-networking-2 - core-java-networking-3 core-java-nio core-java-nio-2 core-java-optional diff --git a/pom.xml b/pom.xml index 845b32615a..a2bd38bc06 100644 --- a/pom.xml +++ b/pom.xml @@ -1322,6 +1322,7 @@ core-java-modules/core-java-string-operations-3 core-java-modules/core-java-string-operations-4 core-java-modules/core-java-time-measurements + core-java-modules/core-java-networking-3 core-java-modules/multimodulemavenproject persistence-modules/sirix quarkus-vs-springboot @@ -1375,6 +1376,7 @@ core-java-modules/core-java-os core-java-modules/core-java-string-operations-3 core-java-modules/core-java-time-measurements + core-java-modules/core-java-networking-3 core-java-modules/multimodulemavenproject core-java-modules/core-java-strings persistence-modules/sirix