diff --git a/src/com/urise/webapp/ResumeTestData.java b/src/com/urise/webapp/ResumeTestData.java index 73e3e4f..a3092a5 100644 --- a/src/com/urise/webapp/ResumeTestData.java +++ b/src/com/urise/webapp/ResumeTestData.java @@ -1,13 +1,14 @@ package com.urise.webapp; -import com.urise.webapp.model.ContactType; -import com.urise.webapp.model.Resume; -import com.urise.webapp.model.Section; -import com.urise.webapp.model.SectionType; +import com.urise.webapp.model.*; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public class ResumeTestData { + private ResumeTestData() {} + public static Resume createResume(String uuid, String fullName) { Resume myResume = new Resume(uuid, fullName); @@ -21,7 +22,7 @@ public static Resume createResume(String uuid, String fullName) { myContacts.put(ContactType.HOME_PAGE, "http://gkislin.ru/"); Map mySections = myResume.getSections(); -/* + // OBJECTIVE section String myObjective = "Ведущий стажировок и корпоративного обучения по Java Web и Enterprise технологиям"; mySections.put(SectionType.OBJECTIVE, new TextSection(myObjective)); @@ -82,7 +83,7 @@ public static Resume createResume(String uuid, String fullName) { "архитектурных шаблонов, UML, функционального программирования"); myQualifications.add("Родной русский, английский \"upper intermediate\""); mySections.put(SectionType.QUALIFICATION, new ListSection(myQualifications)); - +/* // EXPERIENCE section List myCompanies = new ArrayList<>(); myCompanies.add(new Company("Java Online Projects", "http://javaops.ru/", diff --git a/src/com/urise/webapp/model/ContactType.java b/src/com/urise/webapp/model/ContactType.java index 3a04374..a503c3b 100644 --- a/src/com/urise/webapp/model/ContactType.java +++ b/src/com/urise/webapp/model/ContactType.java @@ -8,17 +8,44 @@ public enum ContactType { PHONE("Тел."), MOBILE("Мобильный"), HOME_PHONE("Домашний тел."), - SKYPE("Skype"), - MAIL("Почта"), - LINKEDIN("Профиль LinkedIn"), - GITHUB("Профиль GitHub"), - STACKOVERFLOW("Профиль Stackoverflow"), - HOME_PAGE("Домашняя страница"); + SKYPE("Skype") { + @Override + public String toHtml0(String value) { + return getTitle() + ": " + toLink("skype:" + value, value); + } + }, + MAIL("Почта") { + @Override + public String toHtml0(String value) { + return getTitle() + ": " + toLink("mailto:" + value, value); + } + }, + LINKEDIN("Профиль LinkedIn") { + @Override + public String toHtml0(String value) { + return toLink(value); + } + }, + GITHUB("Профиль GitHub") { + @Override + public String toHtml0(String value) { + return toLink(value); + } + }, + STACKOVERFLOW("Профиль Stackoverflow") { + @Override + public String toHtml0(String value) { + return toLink(value); + } + }, + HOME_PAGE("Домашняя страница") { + @Override + public String toHtml0(String value) { + return toLink(value); + } + }; - private String title; - - ContactType() { - } + private final String title; ContactType(String title) { this.title = title; @@ -27,4 +54,20 @@ public enum ContactType { public String getTitle() { return title; } + + protected String toHtml0(String value) { + return title + ": " + value; + } + + public String toHtml(String value) { + return (value == null) ? "" : toHtml0(value); + } + + public String toLink(String href) { + return toLink(href, title); + } + + public static String toLink(String href, String title) { + return "" + title + ""; + } } diff --git a/src/com/urise/webapp/model/Resume.java b/src/com/urise/webapp/model/Resume.java index 6f6d9de..3e04a87 100644 --- a/src/com/urise/webapp/model/Resume.java +++ b/src/com/urise/webapp/model/Resume.java @@ -94,4 +94,12 @@ public void addContact(ContactType contactType, String value) { public void addSections(SectionType type, Section section) { sections.put(type, section); } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getContact(ContactType contactType) { + return contacts.get(contactType); + } } diff --git a/src/com/urise/webapp/storage/SqlStorage.java b/src/com/urise/webapp/storage/SqlStorage.java index a708f1f..f014c92 100644 --- a/src/com/urise/webapp/storage/SqlStorage.java +++ b/src/com/urise/webapp/storage/SqlStorage.java @@ -1,12 +1,15 @@ package com.urise.webapp.storage; import com.urise.webapp.exception.NotExistStorageException; -import com.urise.webapp.model.ContactType; -import com.urise.webapp.model.Resume; +import com.urise.webapp.model.*; import com.urise.webapp.sql.SqlHelper; +import com.urise.webapp.util.JsonParser; import java.sql.*; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; public class SqlStorage implements Storage { public final SqlHelper sqlHelper; @@ -22,20 +25,33 @@ public void clear() { @Override public Resume get(String uuid) { - return sqlHelper.execute("" + - " SELECT * FROM resume r " + - " LEFT JOIN contact c " + - " ON r.uuid = c.resume_uuid " + - " WHERE r.uuid =? ", ps -> { - ps.setString(1, uuid); - ResultSet rs = ps.executeQuery(); - if (!rs.next()) { - throw new NotExistStorageException(uuid); + return sqlHelper.transactionalExecute(conn -> { + Resume r; + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM resume WHERE uuid =?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) { + throw new NotExistStorageException(uuid); + } + r = new Resume(uuid, rs.getString("full_name")); + } + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM contact WHERE resume_uuid =?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + addContact(rs, r); + } } - Resume r = new Resume(uuid, rs.getString("full_name")); - do { - addContact(rs, r); - } while (rs.next()); + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM section WHERE resume_uuid =?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + addSection(rs, r); + } + } + return r; }); } @@ -43,16 +59,17 @@ public Resume get(String uuid) { @Override public void update(Resume r) { sqlHelper.transactionalExecute(conn -> { - try (PreparedStatement ps = conn.prepareStatement( - "UPDATE resume SET full_name = ? WHERE uuid = ?")) { + try (PreparedStatement ps = conn.prepareStatement("UPDATE resume SET full_name = ? WHERE uuid = ?")) { ps.setString(1, r.getFullName()); ps.setString(2, r.getUuid()); - if (ps.executeUpdate() == 0) { + if (ps.executeUpdate() != 1) { throw new NotExistStorageException(r.getUuid()); } } - delContacts(r, conn); - addContacts(r, conn); + deleteContacts(conn, r); + deleteSections(conn, r); + insertContacts(conn, r); + insertSections(conn, r); return null; }); } @@ -60,15 +77,16 @@ public void update(Resume r) { @Override public void save(Resume r) { sqlHelper.transactionalExecute(conn -> { - try (PreparedStatement ps = conn.prepareStatement( - "INSERT INTO resume (full_name, uuid) VALUES (?,?)")) { - ps.setString(1, r.getFullName()); - ps.setString(2, r.getUuid()); - ps.execute(); - } - addContacts(r, conn); - return null; - }); + try (PreparedStatement ps = conn.prepareStatement("INSERT INTO resume (uuid, full_name) VALUES (?,?)")) { + ps.setString(1, r.getUuid()); + ps.setString(2, r.getFullName()); + ps.execute(); + } + insertContacts(conn, r); + insertSections(conn, r); + return null; + } + ); } @Override @@ -84,25 +102,34 @@ public void delete(String uuid) { @Override public List getAllSorted() { - return sqlHelper.execute("" + - " SELECT * FROM resume r " + - " LEFT JOIN contact c " + - " ON r.uuid = c.resume_uuid " + - " ORDER BY full_name, uuid", ps -> { - ResultSet rs = ps.executeQuery(); - Map resumeMap = new LinkedHashMap<>(); - if (rs.next()) { - do { + return sqlHelper.transactionalExecute(conn -> { + Map resumes = new LinkedHashMap<>(); + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM resume ORDER BY full_name, uuid")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { String uuid = rs.getString("uuid"); - Resume r = resumeMap.get(uuid); - if (r == null) { - r = new Resume(uuid, rs.getString("full_name")); - resumeMap.put(uuid, r); - } + resumes.put(uuid, new Resume(uuid, rs.getString("full_name"))); + } + } + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM contact")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Resume r = resumes.get(rs.getString("resume_uuid")); addContact(rs, r); - } while (rs.next()); + } + } + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM section")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Resume r = resumes.get(rs.getString("resume_uuid")); + addSection(rs, r); + } } - return resumeMap.values().stream().toList(); + + return new ArrayList<>(resumes.values()); }); } @@ -114,31 +141,82 @@ public int size() { }); } - private static void addContact(ResultSet rs, Resume r) throws SQLException { - String type = rs.getString("type"); - if (type != null) { - r.addContact(ContactType.valueOf(type), rs.getString("value")); + private void insertContacts(Connection conn, Resume r) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement("INSERT INTO contact (resume_uuid, type, value) VALUES (?,?,?)")) { + for (Map.Entry e : r.getContacts().entrySet()) { + ps.setString(1, r.getUuid()); + ps.setString(2, e.getKey().name()); + ps.setString(3, e.getValue()); + ps.addBatch(); + } + ps.executeBatch(); } } - private static void addContacts(Resume r, Connection conn) throws SQLException { - try (PreparedStatement ps = conn.prepareStatement( - "INSERT INTO contact (resume_uuid, type, value) VALUES (?,?,?)")) { - for (Map.Entry e : r.getContacts().entrySet()) { + private void insertSections(Connection conn, Resume r) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement("INSERT INTO section (resume_uuid, type, value) VALUES (?,?,?)")) { + for (Map.Entry e : r.getSections().entrySet()) { ps.setString(1, r.getUuid()); ps.setString(2, e.getKey().name()); - ps.setString(3, e.getValue()); + Section section = e.getValue(); + ps.setString(3, JsonParser.write(section, Section.class)); ps.addBatch(); } ps.executeBatch(); } } - private void delContacts(Resume r, Connection conn) throws SQLException { - try (PreparedStatement ps = conn.prepareStatement( - "DELETE FROM contact WHERE resume_uuid=?")) { + private void deleteContacts(Connection conn, Resume r) throws SQLException { + deleteAttributes(conn, r, "DELETE FROM contact WHERE resume_uuid=?"); + } + + private void deleteSections(Connection conn, Resume r) throws SQLException { + deleteAttributes(conn, r, "DELETE FROM section WHERE resume_uuid=?"); + } + + private void deleteAttributes(Connection conn, Resume r, String sql) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, r.getUuid()); - ps.executeUpdate(); + ps.execute(); + } + } + + private void addAllContacts(Map map) { + sqlHelper.execute("SELECT * FROM contact", + ps -> { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + addContact(rs, map.get(rs.getString("resume_uuid"))); + } + return null; + }); + } + + private void addContacts(Resume r) { + sqlHelper.execute( + "SELECT * FROM contact c WHERE c.resume_uuid =?", + ps -> { + ps.setString(1, r.getUuid()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + addContact(rs, r); + } + return null; + }); + } + + private void addContact(ResultSet rs, Resume r) throws SQLException { + String value = rs.getString("value"); + if (value != null) { + r.addContact(ContactType.valueOf(rs.getString("type")), value); + } + } + + private void addSection(ResultSet rs, Resume r) throws SQLException { + String content = rs.getString("value"); + if (content != null) { + SectionType type = SectionType.valueOf(rs.getString("type")); + r.addSections(type, JsonParser.read(content, Section.class)); } } diff --git a/src/com/urise/webapp/util/JsonParser.java b/src/com/urise/webapp/util/JsonParser.java index d6dd5a1..f908261 100644 --- a/src/com/urise/webapp/util/JsonParser.java +++ b/src/com/urise/webapp/util/JsonParser.java @@ -6,12 +6,10 @@ import java.io.Reader; import java.io.Writer; -import java.time.LocalDate; public class JsonParser { - private static final Gson GSON = new GsonBuilder() - .registerTypeAdapter(Section.class, new JsonSectionAdapter<>()) - .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter()) + private static Gson GSON = new GsonBuilder() + .registerTypeAdapter(Section.class, new JsonSectionAdapter()) .create(); public static T read(Reader reader, Class clazz) { @@ -21,4 +19,16 @@ public static T read(Reader reader, Class clazz) { public static void write(T object, Writer writer) { GSON.toJson(object, writer); } -} + + public static T read(String content, Class clazz) { + return GSON.fromJson(content, clazz); + } + + public static String write(T object) { + return GSON.toJson(object); + } + + public static String write(T object, Class clazz) { + return GSON.toJson(object, clazz); + } +} \ No newline at end of file diff --git a/src/com/urise/webapp/util/JsonSectionAdapter.java b/src/com/urise/webapp/util/JsonSectionAdapter.java index 13ddbd7..2113163 100644 --- a/src/com/urise/webapp/util/JsonSectionAdapter.java +++ b/src/com/urise/webapp/util/JsonSectionAdapter.java @@ -22,6 +22,7 @@ public T deserialize(JsonElement json, Type type, JsonDeserializationContext con } } + @Override public JsonElement serialize(T section, Type type, JsonSerializationContext context) { JsonObject retValue = new JsonObject(); diff --git a/src/com/urise/webapp/web/ResumeServlet.java b/src/com/urise/webapp/web/ResumeServlet.java new file mode 100644 index 0000000..ad6e6c6 --- /dev/null +++ b/src/com/urise/webapp/web/ResumeServlet.java @@ -0,0 +1,69 @@ +package com.urise.webapp.web; + +import com.urise.webapp.Config; +import com.urise.webapp.model.ContactType; +import com.urise.webapp.model.Resume; +import com.urise.webapp.storage.Storage; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +public class ResumeServlet extends HttpServlet { + + private Storage storage; // = Config.get().getStorage(); + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + storage = Config.get().getStorage(); + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.setCharacterEncoding("UTF-8"); + String uuid = request.getParameter("uuid"); + String fullName = request.getParameter("fullName"); + Resume r = storage.get(uuid); + r.setFullName(fullName); + for (ContactType type : ContactType.values()) { + String value = request.getParameter(type.name()); + if (value != null && value.trim().length() != 0) { + r.addContact(type, value); + } else { + r.getContacts().remove(type); + } + } + storage.update(r); + response.sendRedirect("resume"); + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String uuid = request.getParameter("uuid"); + String action = request.getParameter("action"); + if (action == null) { + request.setAttribute("resumes", storage.getAllSorted()); + request.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(request, response); + return; + } + Resume r; + switch (action) { + case "delete": + storage.delete(uuid); + response.sendRedirect("resume"); + return; + case "view": + case "edit": + r = storage.get(uuid); + break; + default: + throw new IllegalArgumentException("Action " + action + " is illegal"); + } + request.setAttribute("resume", r); + request.getRequestDispatcher( + ("view".equals(action) ? "/WEB-INF/jsp/view.jsp" : "/WEB-INF/jsp/edit.jsp") + ).forward(request, response); + } +} \ No newline at end of file diff --git a/test/com/urise/webapp/storage/AbstractStorageTest.java b/test/com/urise/webapp/storage/AbstractStorageTest.java index c251622..0bda946 100644 --- a/test/com/urise/webapp/storage/AbstractStorageTest.java +++ b/test/com/urise/webapp/storage/AbstractStorageTest.java @@ -3,12 +3,14 @@ import com.urise.webapp.Config; import com.urise.webapp.exception.ExistStorageException; import com.urise.webapp.exception.NotExistStorageException; +import com.urise.webapp.model.ContactType; import com.urise.webapp.model.Resume; import org.junit.Before; import org.junit.Test; import java.io.File; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -63,6 +65,8 @@ public void clear() throws Exception { @Test public void update() throws Exception { Resume newResume = createResume(UUID_1, "newFirstResume"); + newResume.addContact(ContactType.HOME_PAGE, "http://gkislin.ru/"); + newResume.getContacts().put(ContactType.MAIL, "edarg@wsfws.ru"); storage.update(newResume); Resume oldRes = storage.get(UUID_1); assertEquals(newResume, oldRes); @@ -77,7 +81,9 @@ public void updateNotExist() throws Exception { public void getAllSorted() throws Exception { List list = storage.getAllSorted(); assertEquals(3, list.size()); - assertEquals(list, Arrays.asList(RESUME_1, RESUME_2, RESUME_3)); + List sortedList = Arrays.asList(RESUME_1, RESUME_2, RESUME_3); + Collections.sort(sortedList); + assertEquals(sortedList, list); } @Test