diff --git a/libraries/pom.xml b/libraries/pom.xml
index 4f90d63d93..663b9e68bb 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -709,6 +709,20 @@
xchart
${xchart-version}
+
+
+ commons-net
+ commons-net
+ ${commons-net.version}
+
+
+ org.mockftpserver
+ MockFtpServer
+ ${mockftpserver.version}
+ test
+
+
+
@@ -923,6 +937,8 @@
2.5.11
3.6.1
3.5.2
+ 3.6
+ 2.7.1
\ No newline at end of file
diff --git a/libraries/src/main/java/com/baeldung/ftp/FtpClient.java b/libraries/src/main/java/com/baeldung/ftp/FtpClient.java
new file mode 100644
index 0000000000..209bed35f0
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/ftp/FtpClient.java
@@ -0,0 +1,63 @@
+package com.baeldung.ftp;
+
+import org.apache.commons.net.PrintCommandListener;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPReply;
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+class FtpClient {
+
+ private final String server;
+ private final int port;
+ private final String user;
+ private final String password;
+ private FTPClient ftp;
+
+ FtpClient(String server, int port, String user, String password) {
+ this.server = server;
+ this.port = port;
+ this.user = user;
+ this.password = password;
+ }
+
+ void open() throws IOException {
+ ftp = new FTPClient();
+
+ ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
+
+ ftp.connect(server, port);
+ int reply = ftp.getReplyCode();
+ if (!FTPReply.isPositiveCompletion(reply)) {
+ ftp.disconnect();
+ throw new IOException("Exception in connecting to FTP Server");
+ }
+
+ ftp.login(user, password);
+ }
+
+ void close() throws IOException {
+ ftp.disconnect();
+ }
+
+ Collection listFiles(String path) throws IOException {
+ FTPFile[] files = ftp.listFiles(path);
+
+ return Arrays.stream(files)
+ .map(FTPFile::getName)
+ .collect(Collectors.toList());
+ }
+
+ void putFileToPath(File file, String path) throws IOException {
+ ftp.storeFile(path, new FileInputStream(file));
+ }
+
+ void downloadFile(String source, String destination) throws IOException {
+ FileOutputStream out = new FileOutputStream(destination);
+ ftp.retrieveFile(source, out);
+ }
+}
diff --git a/libraries/src/main/java/com/baeldung/javalin/User/UserController.java b/libraries/src/main/java/com/baeldung/javalin/User/UserController.java
index fd713606cf..685890c6d7 100644
--- a/libraries/src/main/java/com/baeldung/javalin/User/UserController.java
+++ b/libraries/src/main/java/com/baeldung/javalin/User/UserController.java
@@ -14,7 +14,7 @@ public class UserController {
public static Handler fetchById = ctx -> {
int id = Integer.parseInt(Objects.requireNonNull(ctx.param("id")));
UserDao dao = UserDao.instance();
- User user = dao.getUserById(id);
+ User user = dao.getUserById(id).get();
if (user == null) {
ctx.html("Not Found");
} else {
diff --git a/libraries/src/test/java/com/baeldung/ftp/FtpClientIntegrationTest.java b/libraries/src/test/java/com/baeldung/ftp/FtpClientIntegrationTest.java
new file mode 100644
index 0000000000..43da69f96d
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/ftp/FtpClientIntegrationTest.java
@@ -0,0 +1,73 @@
+package com.baeldung.ftp;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockftpserver.fake.FakeFtpServer;
+import org.mockftpserver.fake.UserAccount;
+import org.mockftpserver.fake.filesystem.DirectoryEntry;
+import org.mockftpserver.fake.filesystem.FileEntry;
+import org.mockftpserver.fake.filesystem.FileSystem;
+import org.mockftpserver.fake.filesystem.UnixFakeFileSystem;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FtpClientIntegrationTest {
+
+ private FakeFtpServer fakeFtpServer;
+
+ private FtpClient ftpClient;
+
+ @Before
+ public void setup() throws IOException {
+ fakeFtpServer = new FakeFtpServer();
+ fakeFtpServer.addUserAccount(new UserAccount("user", "password", "/data"));
+
+ FileSystem fileSystem = new UnixFakeFileSystem();
+ fileSystem.add(new DirectoryEntry("/data"));
+ fileSystem.add(new FileEntry("/data/foobar.txt", "abcdef 1234567890"));
+ fakeFtpServer.setFileSystem(fileSystem);
+ fakeFtpServer.setServerControlPort(0);
+
+ fakeFtpServer.start();
+
+ ftpClient = new FtpClient("localhost", fakeFtpServer.getServerControlPort(), "user", "password");
+ ftpClient.open();
+ }
+
+ @After
+ public void teardown() throws IOException {
+ ftpClient.close();
+ fakeFtpServer.stop();
+ }
+
+ @Test
+ public void givenRemoteFile_whenListingRemoteFiles_thenItIsContainedInList() throws IOException {
+ Collection files = ftpClient.listFiles("");
+
+ assertThat(files).contains("foobar.txt");
+ }
+
+ @Test
+ public void givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem() throws IOException {
+ ftpClient.downloadFile("/foobar.txt", "downloaded_buz.txt");
+
+ assertThat(new File("downloaded_buz.txt")).exists();
+ new File("downloaded_buz.txt").delete(); // cleanup
+ }
+
+ @Test
+ public void givenLocalFile_whenUploadingIt_thenItExistsOnRemoteLocation() throws URISyntaxException, IOException {
+ File file = new File(getClass().getClassLoader().getResource("ftp/baz.txt").toURI());
+
+ ftpClient.putFileToPath(file, "/buz.txt");
+
+ assertThat(fakeFtpServer.getFileSystem().exists("/buz.txt")).isTrue();
+ }
+
+}
diff --git a/libraries/src/test/java/com/baeldung/ftp/JdkFtpClientIntegrationTest.java b/libraries/src/test/java/com/baeldung/ftp/JdkFtpClientIntegrationTest.java
new file mode 100644
index 0000000000..ef6809b02d
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/ftp/JdkFtpClientIntegrationTest.java
@@ -0,0 +1,63 @@
+package com.baeldung.ftp;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockftpserver.fake.FakeFtpServer;
+import org.mockftpserver.fake.UserAccount;
+import org.mockftpserver.fake.filesystem.DirectoryEntry;
+import org.mockftpserver.fake.filesystem.FileEntry;
+import org.mockftpserver.fake.filesystem.FileSystem;
+import org.mockftpserver.fake.filesystem.UnixFakeFileSystem;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.util.Collection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class JdkFtpClientIntegrationTest {
+
+ private FakeFtpServer fakeFtpServer;
+
+
+ @Before
+ public void setup() throws IOException {
+ fakeFtpServer = new FakeFtpServer();
+ fakeFtpServer.addUserAccount(new UserAccount("user", "password", "/data"));
+
+ FileSystem fileSystem = new UnixFakeFileSystem();
+ fileSystem.add(new DirectoryEntry("/data"));
+ fileSystem.add(new FileEntry("/data/foobar.txt", "abcdef 1234567890"));
+ fakeFtpServer.setFileSystem(fileSystem);
+ fakeFtpServer.setServerControlPort(0);
+
+ fakeFtpServer.start();
+ }
+
+ @After
+ public void teardown() throws IOException {
+ fakeFtpServer.stop();
+ }
+
+ @Test
+ public void givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem() throws IOException {
+ String ftpUrl = String.format("ftp://user:password@localhost:%d/foobar.txt", fakeFtpServer.getServerControlPort());
+
+ URLConnection urlConnection = new URL(ftpUrl).openConnection();
+ InputStream inputStream = urlConnection.getInputStream();
+ Files.copy(inputStream, new File("downloaded_buz.txt").toPath());
+ inputStream.close();
+
+ assertThat(new File("downloaded_buz.txt")).exists();
+
+ new File("downloaded_buz.txt").delete(); // cleanup
+ }
+
+}
diff --git a/libraries/src/test/resources/ftp/baz.txt b/libraries/src/test/resources/ftp/baz.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/spring-boot-ops/README.md b/spring-boot-ops/README.md
index 02c4c100aa..26caccb727 100644
--- a/spring-boot-ops/README.md
+++ b/spring-boot-ops/README.md
@@ -1,3 +1,3 @@
-### Relevant articles
+### Relevant Articles:
- [Deploy a Spring Boot WAR into a Tomcat Server](http://www.baeldung.com/spring-boot-war-tomcat-deploy)