From 895044bfa3ea6b2032ce0ef6e48637ddc6b5232b Mon Sep 17 00:00:00 2001 From: Ulisses Lima Date: Mon, 14 Feb 2022 23:57:46 -0300 Subject: [PATCH 1/6] BAEL-2080 - Check if a user is logged-in with Servlets and JSP * new module: javax-servlets-2 --- javax-servlets-2/README.md | 5 + javax-servlets-2/pom.xml | 60 ++++++++++++ .../java/com/baeldung/user/check/User.java | 80 +++++++++++++++ .../baeldung/user/check/UserCheckFilter.java | 46 +++++++++ .../user/check/UserCheckLoginServlet.java | 56 +++++++++++ .../user/check/UserCheckLogoutServlet.java | 26 +++++ .../baeldung/user/check/UserCheckServlet.java | 28 ++++++ .../src/main/resources/logback.xml | 13 +++ .../main/webapp/WEB-INF/user.check/home.jsp | 31 ++++++ .../main/webapp/WEB-INF/user.check/login.jsp | 33 +++++++ .../user/check/UserCheckServletLiveTest.java | 98 +++++++++++++++++++ 11 files changed, 476 insertions(+) create mode 100644 javax-servlets-2/README.md create mode 100644 javax-servlets-2/pom.xml create mode 100644 javax-servlets-2/src/main/java/com/baeldung/user/check/User.java create mode 100644 javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckFilter.java create mode 100644 javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLoginServlet.java create mode 100644 javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLogoutServlet.java create mode 100644 javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckServlet.java create mode 100644 javax-servlets-2/src/main/resources/logback.xml create mode 100644 javax-servlets-2/src/main/webapp/WEB-INF/user.check/home.jsp create mode 100644 javax-servlets-2/src/main/webapp/WEB-INF/user.check/login.jsp create mode 100644 javax-servlets-2/src/test/java/com/baeldung/user/check/UserCheckServletLiveTest.java diff --git a/javax-servlets-2/README.md b/javax-servlets-2/README.md new file mode 100644 index 0000000000..f126f17297 --- /dev/null +++ b/javax-servlets-2/README.md @@ -0,0 +1,5 @@ +## Servlets + +This module contains articles about Servlets. + +### Relevant Articles: diff --git a/javax-servlets-2/pom.xml b/javax-servlets-2/pom.xml new file mode 100644 index 0000000000..34c00c3d05 --- /dev/null +++ b/javax-servlets-2/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + com.baeldung.javax-servlets + javax-servlets-2 + 1.0-SNAPSHOT + javax-servlets-2 + war + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + + commons-fileupload + commons-fileupload + ${commons-fileupload.version} + + + + javax.servlet + javax.servlet-api + ${javax.servlet-api.version} + + + javax.servlet.jsp + javax.servlet.jsp-api + ${javax.servlet.jsp-api.version} + provided + + + javax.servlet + jstl + ${jstl.version} + + + org.apache.httpcomponents + httpclient + ${org.apache.httpcomponents.version} + test + + + commons-logging + commons-logging + + + + + + + 4.5.13 + 4.0.1 + + diff --git a/javax-servlets-2/src/main/java/com/baeldung/user/check/User.java b/javax-servlets-2/src/main/java/com/baeldung/user/check/User.java new file mode 100644 index 0000000000..f61c0490bc --- /dev/null +++ b/javax-servlets-2/src/main/java/com/baeldung/user/check/User.java @@ -0,0 +1,80 @@ +package com.baeldung.user.check; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +/** + * @since 6 de fev de 2022 + * @author ulisses + */ +public class User implements Serializable { + private static final long serialVersionUID = 1L; + + protected static final HashMap DB = new HashMap<>(); + static { + DB.put("admin", new User("admin", "password")); + DB.put("user", new User("user", "pass")); + } + + private String name; + private String password; + + private List logins = new ArrayList(); + + public User(String name, String password) { + this.name = name; + this.password = password; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getLogins() { + return logins; + } + + public void setLogins(List logins) { + this.logins = logins; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + User other = (User) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } +} diff --git a/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckFilter.java b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckFilter.java new file mode 100644 index 0000000000..2a370afe85 --- /dev/null +++ b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckFilter.java @@ -0,0 +1,46 @@ +package com.baeldung.user.check; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebFilter("/user-check/*") +public class UserCheckFilter implements Filter { + public static void forward(HttpServletRequest request, HttpServletResponse response, String page) throws ServletException, IOException { + request.getRequestDispatcher("/WEB-INF/user.check" + page) + .forward(request, response); + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + if (!(req instanceof HttpServletRequest)) { + throw new ServletException("Can only process HttpServletRequest"); + } + + if (!(res instanceof HttpServletResponse)) { + throw new ServletException("Can only process HttpServletResponse"); + } + + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + request.setAttribute("origin", request.getRequestURI()); + + if (!request.getRequestURI() + .contains("login") && request.getSession(false) == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + forward(request, response, "/login.jsp"); + // we return here so the original servlet is not processed + return; + } + + chain.doFilter(request, response); + } +} diff --git a/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLoginServlet.java b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLoginServlet.java new file mode 100644 index 0000000000..e1a38fc7b8 --- /dev/null +++ b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLoginServlet.java @@ -0,0 +1,56 @@ +package com.baeldung.user.check; + +import java.io.IOException; +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@WebServlet("/user-check/login") +public class UserCheckLoginServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (request.getSession(false) != null) { + response.sendRedirect(request.getContextPath() + "/user-check/home"); + return; + } + + String referer = (String) request.getAttribute("origin"); + request.setAttribute("origin", referer); + UserCheckFilter.forward(request, response, "/login.jsp"); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String key = request.getParameter("name"); + String pass = request.getParameter("password"); + + User user = User.DB.get(key); + if (user == null || !user.getPassword() + .equals(pass)) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + request.setAttribute("origin", request.getParameter("origin")); + request.setAttribute("error", "invalid login"); + UserCheckFilter.forward(request, response, "/login.jsp"); + return; + } + + user.getLogins() + .add(new Date()); + + HttpSession session = request.getSession(); + session.setAttribute("user", user); + + String origin = request.getParameter("origin"); + if (origin == null || origin.contains("login")) + origin = "./"; + + response.sendRedirect(origin); + } +} diff --git a/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLogoutServlet.java b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLogoutServlet.java new file mode 100644 index 0000000000..42c0bb87ab --- /dev/null +++ b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckLogoutServlet.java @@ -0,0 +1,26 @@ +package com.baeldung.user.check; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@WebServlet("/user-check/logout") +public class UserCheckLogoutServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + HttpSession session = request.getSession(false); + if (session != null) { + session.invalidate(); + } + + request.setAttribute("loggedOut", true); + response.sendRedirect("./"); + } +} diff --git a/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckServlet.java b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckServlet.java new file mode 100644 index 0000000000..d7d5d1e762 --- /dev/null +++ b/javax-servlets-2/src/main/java/com/baeldung/user/check/UserCheckServlet.java @@ -0,0 +1,28 @@ +package com.baeldung.user.check; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@WebServlet(name = "home", urlPatterns = { "/user-check/", "/user-check", "/user-check/home" }) +public class UserCheckServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + HttpSession session = request.getSession(false); + if (session == null || session.getAttribute("user") == null) { + throw new IllegalStateException("user not logged in"); + } + + User user = (User) session.getAttribute("user"); + request.setAttribute("user", user); + + UserCheckFilter.forward(request, response, "/home.jsp"); + } +} diff --git a/javax-servlets-2/src/main/resources/logback.xml b/javax-servlets-2/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/javax-servlets-2/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/javax-servlets-2/src/main/webapp/WEB-INF/user.check/home.jsp b/javax-servlets-2/src/main/webapp/WEB-INF/user.check/home.jsp new file mode 100644 index 0000000000..4e17763552 --- /dev/null +++ b/javax-servlets-2/src/main/webapp/WEB-INF/user.check/home.jsp @@ -0,0 +1,31 @@ +<%@ page contentType="text/html;charset=UTF-8" session="false"%> +<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%> + + + + login success - current session info + + +
+

user info

+
+ name: ${user.name} +
+ +
+ logins: +
    + +
  • ${login}
  • +
    +
+
+ + +
+ + diff --git a/javax-servlets-2/src/main/webapp/WEB-INF/user.check/login.jsp b/javax-servlets-2/src/main/webapp/WEB-INF/user.check/login.jsp new file mode 100644 index 0000000000..19a857585d --- /dev/null +++ b/javax-servlets-2/src/main/webapp/WEB-INF/user.check/login.jsp @@ -0,0 +1,33 @@ +<%@ page contentType="text/html;charset=UTF-8" session="false"%> +<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%> + + + + login + + +
+ + +
* redirected to login from: ${origin}
+
+ + +
* error: ${error}
+
+ +
+ credentials + + + + + + + + +
+
+ + diff --git a/javax-servlets-2/src/test/java/com/baeldung/user/check/UserCheckServletLiveTest.java b/javax-servlets-2/src/test/java/com/baeldung/user/check/UserCheckServletLiveTest.java new file mode 100644 index 0000000000..42858d61e7 --- /dev/null +++ b/javax-servlets-2/src/test/java/com/baeldung/user/check/UserCheckServletLiveTest.java @@ -0,0 +1,98 @@ +package com.baeldung.user.check; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.LaxRedirectStrategy; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class UserCheckServletLiveTest { + private static final String BASE_URL = "http://localhost:8080/javax-servlets-2/user-check"; + + @Mock + HttpServletRequest request; + + @Mock + HttpServletResponse response; + + private CloseableHttpClient buildClient() { + return HttpClientBuilder.create() + .setRedirectStrategy(new LaxRedirectStrategy()) + .build(); + } + + @Test + public void whenCorrectCredentials_thenLoginSucceeds() throws Exception { + try (CloseableHttpClient client = buildClient()) { + HttpPost post = new HttpPost(BASE_URL + "/login"); + + List form = new ArrayList<>(); + form.add(new BasicNameValuePair("name", "admin")); + form.add(new BasicNameValuePair("password", "password")); + + post.setEntity(new UrlEncodedFormEntity(form)); + try (CloseableHttpResponse response = client.execute(post)) { + String body = EntityUtils.toString(response.getEntity()); + + assertTrue(response.getStatusLine() + .getStatusCode() == 200); + + assertTrue(body.contains("login success")); + } + } + } + + @Test + public void whenIncorrectCredentials_thenLoginFails() throws Exception { + try (CloseableHttpClient client = buildClient()) { + HttpPost post = new HttpPost(BASE_URL + "/login"); + + List form = new ArrayList<>(); + form.add(new BasicNameValuePair("name", "admin")); + form.add(new BasicNameValuePair("password", "invalid")); + + post.setEntity(new UrlEncodedFormEntity(form)); + try (CloseableHttpResponse response = client.execute(post)) { + String body = EntityUtils.toString(response.getEntity()); + + assertTrue(response.getStatusLine() + .getStatusCode() == 401); + + assertTrue(body.contains("invalid login")); + } + } + } + + @Test + public void whenNotLoggedIn_thenRedirectedToLoginPage() throws Exception { + try (CloseableHttpClient client = buildClient()) { + HttpGet get = new HttpGet(BASE_URL + "/home"); + + try (CloseableHttpResponse response = client.execute(get)) { + String body = EntityUtils.toString(response.getEntity()); + + assertTrue(response.getStatusLine() + .getStatusCode() == 401); + + assertTrue(body.contains("redirected to login")); + } + } + } +} From b07644ee70a6828ce272434e4a5ab2ef5a9db9b7 Mon Sep 17 00:00:00 2001 From: Ulisses Lima Date: Tue, 22 Feb 2022 15:39:23 -0300 Subject: [PATCH 2/6] adding javax-servlets-2 to the parent pom. --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index f5378f9961..ea2177c4d8 100644 --- a/pom.xml +++ b/pom.xml @@ -943,6 +943,7 @@ java-vavr-stream java-websocket javax-servlets + javax-servlets-2 javaxval jaxb jee-7 From b6db99e3a157296796c853cdf5546d89241bfb1e Mon Sep 17 00:00:00 2001 From: Ulisses Lima Date: Tue, 22 Feb 2022 15:43:49 -0300 Subject: [PATCH 3/6] adding module javax-servlets-2 to all profiles --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea2177c4d8..f3d16366bd 100644 --- a/pom.xml +++ b/pom.xml @@ -459,6 +459,7 @@ java-vavr-stream java-websocket javax-servlets + javax-servlets-2 javaxval jaxb jee-7 @@ -943,7 +944,7 @@ java-vavr-stream java-websocket javax-servlets - javax-servlets-2 + javax-servlets-2 javaxval jaxb jee-7 From 5b56b1ed392e3448f940601dd74367b88577a0d1 Mon Sep 17 00:00:00 2001 From: Ulisses Lima Date: Fri, 11 Mar 2022 20:11:54 -0300 Subject: [PATCH 4/6] BAEL-5403 - Import Data to MongoDB from Json File using Java * Project * Integration tests --- .../SpringBootPersistenceApplication.java | 48 +++++++- .../boot/json/convertfile/ImportUtils.java | 60 ++++++++++ .../json/convertfile/dao/BookRepository.java | 9 ++ .../boot/json/convertfile/data/Book.java | 38 +++++++ .../service/ImportJsonService.java | 74 ++++++++++++ .../json/convertfile/web/BookController.java | 51 +++++++++ .../convertfile/web/ImportJsonController.java | 35 ++++++ .../boot.json.convertfile/data.json.log | 3 + .../ImportJsonServiceIntegrationTest.java | 105 ++++++++++++++++++ .../boot.json.convertfile/books.json.log | 2 + .../boot.json.convertfile/movies.json.log | 3 + 11 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/ImportUtils.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/dao/BookRepository.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/data/Book.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/service/ImportJsonService.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/BookController.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/ImportJsonController.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/resources/boot.json.convertfile/data.json.log create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/boot/json/convertfile/service/ImportJsonServiceIntegrationTest.java create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/books.json.log create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/movies.json.log diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java index 2dff3f37df..a5cb443291 100644 --- a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java @@ -1,13 +1,59 @@ package com.baeldung; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +import com.baeldung.boot.json.convertfile.ImportUtils; +import com.baeldung.boot.json.convertfile.service.ImportJsonService; @SpringBootApplication -public class SpringBootPersistenceApplication { +public class SpringBootPersistenceApplication implements ApplicationRunner { + private Logger log = LogManager.getLogger(this.getClass()); + private static final String RESOURCE_PREFIX = "classpath:"; + + @Autowired + private ApplicationContext context; public static void main(String ... args) { SpringApplication.run(SpringBootPersistenceApplication.class, args); } + @Override + public void run(ApplicationArguments args) throws Exception { + if (args.containsOption("import")) { + if (!args.containsOption("collection")) + throw new IllegalArgumentException("required option: --collection with collection name when using --import"); + + String collection = args.getOptionValues("collection") + .get(0); + + List sources = args.getOptionValues("import"); + for (String source : sources) { + List jsonLines = new ArrayList<>(); + if (source.startsWith(RESOURCE_PREFIX)) { + String resource = source.substring(RESOURCE_PREFIX.length()); + jsonLines = ImportUtils.linesFromResource(resource); + } else { + jsonLines = ImportUtils.lines(new File(source)); + } + + if (jsonLines == null || jsonLines.isEmpty()) + throw new IllegalArgumentException("no input to import"); + + ImportJsonService importService = context.getBean(ImportJsonService.class); + String result = importService.importTo(collection, jsonLines); + log.info(source + " - result: " + result); + } + } + } } diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/ImportUtils.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/ImportUtils.java new file mode 100644 index 0000000000..c09746a33d --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/ImportUtils.java @@ -0,0 +1,60 @@ +package com.baeldung.boot.json.convertfile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +public class ImportUtils { + private static Logger log = LogManager.getLogger(ImportUtils.class); + + public static List lines(String json) { + if (json == null) + return Collections.emptyList(); + + String[] split = json.split("[\\r\\n]+"); + return Arrays.asList(split); + } + + public static List lines(MultipartFile file) { + try { + Path tmp = Files.write(File.createTempFile("mongo-upload", null) + .toPath(), file.getBytes()); + + return Files.readAllLines(tmp); + } catch (IOException e) { + log.error("reading lines from " + file, e); + return null; + } + } + + public static List lines(File file) { + try { + return Files.readAllLines(file.toPath()); + } catch (IOException e) { + log.error("reading lines from " + file, e); + return null; + } + } + + public static List linesFromResource(String resource) { + Resource input = new ClassPathResource(resource); + try { + Path path = input.getFile() + .toPath(); + return Files.readAllLines(path); + } catch (IOException e) { + log.error("reading lines from classpath resource: " + resource, e); + return null; + } + } +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/dao/BookRepository.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/dao/BookRepository.java new file mode 100644 index 0000000000..523be429da --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/dao/BookRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.boot.json.convertfile.dao; + +import org.springframework.data.mongodb.repository.MongoRepository; + +import com.baeldung.boot.json.convertfile.data.Book; + +public interface BookRepository extends MongoRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/data/Book.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/data/Book.java new file mode 100644 index 0000000000..c352cdeecd --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/data/Book.java @@ -0,0 +1,38 @@ +package com.baeldung.boot.json.convertfile.data; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document("books") +public class Book { + @Id + private String id; + + private String name; + + private String genre; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGenre() { + return genre; + } + + public void setGenre(String genre) { + this.genre = genre; + } +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/service/ImportJsonService.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/service/ImportJsonService.java new file mode 100644 index 0000000000..4800a85da3 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/service/ImportJsonService.java @@ -0,0 +1,74 @@ +package com.baeldung.boot.json.convertfile.service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bson.Document; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.MongoBulkWriteException; + +@Service +public class ImportJsonService { + private Logger log = LogManager.getLogger(this.getClass()); + + @Autowired + private MongoTemplate mongo; + + public String importTo(Class type, List jsonLines) { + List mongoDocs = generateMongoDocs(jsonLines, type); + String collection = type.getAnnotation(org.springframework.data.mongodb.core.mapping.Document.class) + .value(); + int inserts = insertInto(collection, mongoDocs); + return inserts + "/" + jsonLines.size(); + } + + public String importTo(String collection, List jsonLines) { + List mongoDocs = generateMongoDocs(jsonLines); + int inserts = insertInto(collection, mongoDocs); + return inserts + "/" + jsonLines.size(); + } + + private int insertInto(String collection, List mongoDocs) { + try { + Collection inserts = mongo.insert(mongoDocs, collection); + return inserts.size(); + } catch (DataIntegrityViolationException e) { + log.error("importing docs", e); + if (e.getCause() instanceof MongoBulkWriteException) { + return ((MongoBulkWriteException) e.getCause()).getWriteResult() + .getInsertedCount(); + } + return 0; + } + } + + private List generateMongoDocs(List lines) { + return generateMongoDocs(lines, null); + } + + private List generateMongoDocs(List lines, Class type) { + ObjectMapper mapper = new ObjectMapper(); + + List docs = new ArrayList<>(); + for (String json : lines) { + try { + if (type != null) { + T v = mapper.readValue(json, type); + json = mapper.writeValueAsString(v); + } + docs.add(Document.parse(json)); + } catch (Throwable e) { + log.error("parsing: " + json, e); + } + } + return docs; + } +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/BookController.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/BookController.java new file mode 100644 index 0000000000..3fbe1bb34c --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/BookController.java @@ -0,0 +1,51 @@ +package com.baeldung.boot.json.convertfile.web; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.baeldung.boot.json.convertfile.ImportUtils; +import com.baeldung.boot.json.convertfile.dao.BookRepository; +import com.baeldung.boot.json.convertfile.data.Book; +import com.baeldung.boot.json.convertfile.service.ImportJsonService; + +@RestController +@RequestMapping("/books") +public class BookController { + @Autowired + private BookRepository books; + + @Autowired + private ImportJsonService service; + + @PostMapping + public Book postBook(@RequestBody Book book) throws IOException { + return books.insert(book); + } + + @GetMapping + public List getBooks() { + return books.findAll(); + } + + @GetMapping("/{id}") + public Optional getBook(@PathVariable String id) { + return books.findById(id); + } + + @PostMapping("/import/file") + public String postJsonFile(@RequestPart("parts") MultipartFile jsonStringsFile) throws IOException { + List jsonLines = ImportUtils.lines(jsonStringsFile); + return service.importTo(Book.class, jsonLines); + } +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/ImportJsonController.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/ImportJsonController.java new file mode 100644 index 0000000000..2e475c9e8c --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/web/ImportJsonController.java @@ -0,0 +1,35 @@ +package com.baeldung.boot.json.convertfile.web; + +import java.io.IOException; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.baeldung.boot.json.convertfile.ImportUtils; +import com.baeldung.boot.json.convertfile.service.ImportJsonService; + +@RestController +@RequestMapping("/import-json") +public class ImportJsonController { + @Autowired + private ImportJsonService service; + + @PostMapping("/{collection}") + public String postJson(@RequestBody String jsonStrings, @PathVariable String collection) { + List jsonLines = ImportUtils.lines(jsonStrings); + return service.importTo(collection, jsonLines); + } + + @PostMapping("/file/{collection}") + public String postJsonFile(@RequestPart("parts") MultipartFile jsonStringsFile, @PathVariable String collection) throws IOException { + List jsonLines = ImportUtils.lines(jsonStringsFile); + return service.importTo(collection, jsonLines); + } +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/boot.json.convertfile/data.json.log b/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/boot.json.convertfile/data.json.log new file mode 100644 index 0000000000..4c1a23f435 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/resources/boot.json.convertfile/data.json.log @@ -0,0 +1,3 @@ +{"name":"Book A", "genre": "Comedy"} +{"name":"Book B", "genre": "Thriller"} +{"_id": "mongo-id", "name":"Book C", "genre": "Drama"} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/boot/json/convertfile/service/ImportJsonServiceIntegrationTest.java b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/boot/json/convertfile/service/ImportJsonServiceIntegrationTest.java new file mode 100644 index 0000000000..611cdffa88 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/java/com/baeldung/boot/json/convertfile/service/ImportJsonServiceIntegrationTest.java @@ -0,0 +1,105 @@ +package com.baeldung.boot.json.convertfile.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import com.baeldung.boot.json.convertfile.ImportUtils; +import com.baeldung.boot.json.convertfile.dao.BookRepository; +import com.baeldung.boot.json.convertfile.data.Book; +import com.mongodb.DBObject; + +@SpringBootTest +@DirtiesContext +@RunWith(SpringRunner.class) +public class ImportJsonServiceIntegrationTest { + @Autowired + private ImportJsonService service; + + @Autowired + private MongoTemplate mongoDb; + + @Autowired + BookRepository bookRepository; + + @Test + public void givenJsonString_whenGenericType_thenDocumentImported() { + String collection = "items"; + List docs = mongoDb.findAll(DBObject.class, collection); + int sizeBefore = docs.size(); + + String json = "{\"name\":\"Item A\"}\n{\"name\":\"Item B\"}"; + List jsonLines = ImportUtils.lines(json); + service.importTo(collection, jsonLines); + + docs = mongoDb.findAll(DBObject.class, collection); + int sizeAfter = docs.size(); + assertThat(sizeAfter - sizeBefore).isEqualTo(jsonLines.size()); + } + + @Test + public void givenJsonFile_whenClasspathSource_thenDocumentImported() { + String collection = "movies"; + List docs = mongoDb.findAll(DBObject.class, collection); + int sizeBefore = docs.size(); + + String resource = "boot.json.convertfile/movies.json.log"; + List jsonLines = ImportUtils.linesFromResource(resource); + service.importTo(collection, jsonLines); + + docs = mongoDb.findAll(DBObject.class, collection); + int sizeAfter = docs.size(); + assertThat(sizeAfter - sizeBefore).isEqualTo(jsonLines.size()); + } + + @Test + public void givenJsonClasspathFile_whenCorrectlyTyped_thenDocumentImported() { + List books = bookRepository.findAll(); + int sizeBefore = books.size(); + + String resource = "boot.json.convertfile/books.json.log"; + List jsonLines = ImportUtils.linesFromResource(resource); + service.importTo(Book.class, jsonLines); + + books = bookRepository.findAll(); + int sizeAfter = books.size(); + assertThat(sizeAfter - sizeBefore).isEqualTo(jsonLines.size()); + } + + @Test + public void givenIncorrectlyTypedJson_whenUsingTypes_thenDocumentNotImported() { + List books = bookRepository.findAll(); + int sizeBefore = books.size(); + + String resource = "boot.json.convertfile/movies.json.log"; + List jsonLines = ImportUtils.linesFromResource(resource); + service.importTo(Book.class, jsonLines); + + books = bookRepository.findAll(); + int sizeAfter = books.size(); + assertThat(sizeAfter - sizeBefore).isEqualTo(0); + } + + @Test + public void whenInvalidJson_thenDocumentNotImported() { + String collection = "items"; + List docs = mongoDb.findAll(DBObject.class, collection); + int sizeBefore = docs.size(); + + String json = "{name: Item A}\n{name: Item B}"; + List jsonLines = ImportUtils.lines(json); + service.importTo(collection, jsonLines); + + docs = mongoDb.findAll(DBObject.class, collection); + int sizeAfter = docs.size(); + assertThat(sizeAfter - sizeBefore).isEqualTo(0); + } +} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/books.json.log b/persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/books.json.log new file mode 100644 index 0000000000..63c618ba82 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/books.json.log @@ -0,0 +1,2 @@ +{"name":"Movie A", "genre": "Comedy"} +{"name":"Movie B", "genre": "Thriller"} diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/movies.json.log b/persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/movies.json.log new file mode 100644 index 0000000000..7658d8610e --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/test/resources/boot.json.convertfile/movies.json.log @@ -0,0 +1,3 @@ +{"title":"Movie A", "genre": "Comedy"} +{"title":"Movie B", "genre": "Thriller"} +{"_id": "mongo-id", "title":"Movie C", "genre": "Drama"} From c249317694e4f218517ff177dbe247eafbddd7ef Mon Sep 17 00:00:00 2001 From: Ulisses Lima Date: Mon, 14 Mar 2022 23:34:39 -0300 Subject: [PATCH 5/6] warn instead of throw --- .../baeldung/SpringBootPersistenceApplication.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java index a5cb443291..bbda5823f8 100644 --- a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java @@ -47,12 +47,13 @@ public class SpringBootPersistenceApplication implements ApplicationRunner { jsonLines = ImportUtils.lines(new File(source)); } - if (jsonLines == null || jsonLines.isEmpty()) - throw new IllegalArgumentException("no input to import"); - - ImportJsonService importService = context.getBean(ImportJsonService.class); - String result = importService.importTo(collection, jsonLines); - log.info(source + " - result: " + result); + if (jsonLines == null || jsonLines.isEmpty()) { + log.warn(source + " - no input to import"); + } else { + ImportJsonService importService = context.getBean(ImportJsonService.class); + String result = importService.importTo(collection, jsonLines); + log.info(source + " - import result: " + result); + } } } } From e5c34a56c218efdd6ba2d3a6da2af4ee80cd78a9 Mon Sep 17 00:00:00 2001 From: Ulisses Lima Date: Tue, 22 Mar 2022 19:27:49 -0300 Subject: [PATCH 6/6] https://jira.baeldung.com/browse/BAEL-5403 Editor Review changes: * reverted SpringBootPersistenceApplication.java * created SpringBootJsonConvertFileApplication.java in article package: com.baeldung.boot.json.convertfile --- .../SpringBootPersistenceApplication.java | 49 +--------------- .../SpringBootJsonConvertFileApplication.java | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 48 deletions(-) create mode 100644 persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/SpringBootJsonConvertFileApplication.java diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java index bbda5823f8..2dff3f37df 100644 --- a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/SpringBootPersistenceApplication.java @@ -1,60 +1,13 @@ package com.baeldung; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ApplicationContext; - -import com.baeldung.boot.json.convertfile.ImportUtils; -import com.baeldung.boot.json.convertfile.service.ImportJsonService; @SpringBootApplication -public class SpringBootPersistenceApplication implements ApplicationRunner { - private Logger log = LogManager.getLogger(this.getClass()); - private static final String RESOURCE_PREFIX = "classpath:"; - - @Autowired - private ApplicationContext context; +public class SpringBootPersistenceApplication { public static void main(String ... args) { SpringApplication.run(SpringBootPersistenceApplication.class, args); } - @Override - public void run(ApplicationArguments args) throws Exception { - if (args.containsOption("import")) { - if (!args.containsOption("collection")) - throw new IllegalArgumentException("required option: --collection with collection name when using --import"); - - String collection = args.getOptionValues("collection") - .get(0); - - List sources = args.getOptionValues("import"); - for (String source : sources) { - List jsonLines = new ArrayList<>(); - if (source.startsWith(RESOURCE_PREFIX)) { - String resource = source.substring(RESOURCE_PREFIX.length()); - jsonLines = ImportUtils.linesFromResource(resource); - } else { - jsonLines = ImportUtils.lines(new File(source)); - } - - if (jsonLines == null || jsonLines.isEmpty()) { - log.warn(source + " - no input to import"); - } else { - ImportJsonService importService = context.getBean(ImportJsonService.class); - String result = importService.importTo(collection, jsonLines); - log.info(source + " - import result: " + result); - } - } - } - } } diff --git a/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/SpringBootJsonConvertFileApplication.java b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/SpringBootJsonConvertFileApplication.java new file mode 100644 index 0000000000..40613c056b --- /dev/null +++ b/persistence-modules/spring-boot-persistence-mongodb/src/main/java/com/baeldung/boot/json/convertfile/SpringBootJsonConvertFileApplication.java @@ -0,0 +1,58 @@ +package com.baeldung.boot.json.convertfile; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.baeldung.boot.json.convertfile.service.ImportJsonService; + +@SpringBootApplication +public class SpringBootJsonConvertFileApplication implements ApplicationRunner { + private Logger log = LogManager.getLogger(this.getClass()); + private static final String RESOURCE_PREFIX = "classpath:"; + + @Autowired + private ImportJsonService importService; + + public static void main(String ... args) { + SpringApplication.run(SpringBootJsonConvertFileApplication.class, args); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + if (args.containsOption("import")) { + if (!args.containsOption("collection")) + throw new IllegalArgumentException("required option: --collection with collection name when using --import"); + + String collection = args.getOptionValues("collection") + .get(0); + + List sources = args.getOptionValues("import"); + for (String source : sources) { + List jsonLines = new ArrayList<>(); + if (source.startsWith(RESOURCE_PREFIX)) { + String resource = source.substring(RESOURCE_PREFIX.length()); + jsonLines = ImportUtils.linesFromResource(resource); + } else { + jsonLines = ImportUtils.lines(new File(source)); + } + + if (jsonLines == null || jsonLines.isEmpty()) { + log.warn(source + " - no input to import"); + } else { + // ImportJsonService importService = context.getBean(ImportJsonService.class); + String result = importService.importTo(collection, jsonLines); + log.info(source + " - import result: " + result); + } + } + } + } +}