package com.mzh.library.controller; import com.mzh.library.dao.impl.JdbcBookDao; import com.mzh.library.entity.AuthenticatedUser; import com.mzh.library.entity.Book; import com.mzh.library.entity.BookCategory; import com.mzh.library.entity.BookSearchCriteria; import com.mzh.library.entity.BookStatus; import com.mzh.library.service.BookService; import com.mzh.library.service.ServiceResult; import com.mzh.library.service.impl.BookServiceImpl; import com.mzh.library.util.SessionAttributes; import java.io.IOException; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class BookManagementServlet extends HttpServlet { private static final String MANAGE_JSP = "/WEB-INF/jsp/books/manage.jsp"; private static final String FORM_JSP = "/WEB-INF/jsp/books/form.jsp"; private static final String UNAUTHORIZED_JSP = "/WEB-INF/jsp/auth/unauthorized.jsp"; private static final String FLASH_SUCCESS = "flashSuccess"; private static final String FLASH_ERROR = "flashError"; private BookService bookService; @Override public void init() { this.bookService = new BookServiceImpl(new JdbcBookDao()); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path = request.getServletPath(); if ("/books/new".equals(path)) { renderForm(request, response, "Create book", "/books", new Book(), Collections.emptyMap(), Collections.emptyMap(), null); return; } if ("/books/edit".equals(path)) { showEditForm(request, response); return; } if (!"/books".equals(path)) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } showManagementList(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path = request.getServletPath(); if ("/books".equals(path)) { createBook(request, response); return; } if ("/books/update".equals(path)) { updateBook(request, response); return; } if ("/books/delete".equals(path)) { deleteBook(request, response); return; } response.sendError(HttpServletResponse.SC_NOT_FOUND); } private void showManagementList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BookSearchCriteria criteria = searchCriteria(request); request.setAttribute("criteria", criteria); applyFlash(request); ServiceResult> categoryResult = bookService.listCategories(); request.setAttribute("categories", categoryResult.isSuccessful() ? categoryResult.getData() : Collections.emptyList()); if (!categoryResult.isSuccessful()) { request.setAttribute("errorMessage", categoryResult.getMessage()); } ServiceResult> searchResult = bookService.searchBooks(criteria); request.setAttribute("books", searchResult.isSuccessful() ? searchResult.getData() : Collections.emptyList()); if (!searchResult.isSuccessful()) { request.setAttribute("errorMessage", searchResult.getMessage()); request.setAttribute("errors", searchResult.getErrors()); } request.getRequestDispatcher(MANAGE_JSP).forward(request, response); } private void showEditForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long id = requiredLong(request.getParameter("id"), -1L); ServiceResult> result = bookService.findBook(id); if (!result.isSuccessful() || !result.getData().isPresent()) { flashError(request, result.isSuccessful() ? "Book was not found." : result.getMessage()); response.sendRedirect(request.getContextPath() + "/books"); return; } renderForm(request, response, "Edit book", "/books/update", result.getData().get(), Collections.emptyMap(), Collections.emptyMap(), null); } private void createBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BookForm form = readBookForm(request, false); if (!form.getErrors().isEmpty()) { renderForm(request, response, "Create book", "/books", form.getBook(), form.getValues(), form.getErrors(), "Please correct the highlighted book fields."); return; } ServiceResult result = bookService.createBook(currentUser(request), form.getBook()); if (!result.isSuccessful()) { handleFormFailure(request, response, "Create book", "/books", form, result); return; } flashSuccess(request, result.getMessage()); response.sendRedirect(request.getContextPath() + "/books"); } private void updateBook(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BookForm form = readBookForm(request, true); if (!form.getErrors().isEmpty()) { renderForm(request, response, "Edit book", "/books/update", form.getBook(), form.getValues(), form.getErrors(), "Please correct the highlighted book fields."); return; } ServiceResult result = bookService.updateBook(currentUser(request), form.getBook()); if (!result.isSuccessful()) { handleFormFailure(request, response, "Edit book", "/books/update", form, result); return; } flashSuccess(request, result.getMessage()); response.sendRedirect(request.getContextPath() + "/books"); } private void deleteBook(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { long id = requiredLong(request.getParameter("id"), -1L); ServiceResult result = bookService.deleteBook(currentUser(request), id); if (isPermissionDenied(result)) { forwardDenied(request, response, result.getMessage()); return; } if (result.isSuccessful()) { flashSuccess(request, result.getMessage()); } else { flashError(request, result.getMessage()); } response.sendRedirect(request.getContextPath() + "/books"); } private void handleFormFailure(HttpServletRequest request, HttpServletResponse response, String title, String action, BookForm form, ServiceResult result) throws ServletException, IOException { if (isPermissionDenied(result)) { forwardDenied(request, response, result.getMessage()); return; } renderForm(request, response, title, action, form.getBook(), form.getValues(), result.getErrors(), result.getMessage()); } private void renderForm(HttpServletRequest request, HttpServletResponse response, String title, String action, Book book, Map formValues, Map errors, String errorMessage) throws ServletException, IOException { ServiceResult> categoryResult = bookService.listCategories(); request.setAttribute("categories", categoryResult.isSuccessful() ? categoryResult.getData() : Collections.emptyList()); request.setAttribute("statuses", BookStatus.values()); request.setAttribute("formTitle", title); request.setAttribute("formAction", action); request.setAttribute("book", book); request.setAttribute("formValues", formValues); request.setAttribute("errors", errors); if (errorMessage != null && !errorMessage.isEmpty()) { request.setAttribute("errorMessage", errorMessage); } else if (!categoryResult.isSuccessful()) { request.setAttribute("errorMessage", categoryResult.getMessage()); } request.getRequestDispatcher(FORM_JSP).forward(request, response); } private BookForm readBookForm(HttpServletRequest request, boolean requireId) { Map values = formValues(request); Map errors = new LinkedHashMap<>(); Book book = new Book(); if (requireId) { book.setId(parseLong(values.get("id"), "id", "Select a valid book.", errors)); } book.setIdentifier(values.get("identifier")); book.setTitle(values.get("title")); book.setAuthor(values.get("author")); book.setCategoryId(parseLong(values.get("categoryId"), "categoryId", "Select a category.", errors)); book.setTotalCopies(parseInt(values.get("totalCopies"), "totalCopies", "Enter a valid total copy count.", errors)); book.setAvailableCopies(parseInt(values.get("availableCopies"), "availableCopies", "Enter a valid available copy count.", errors)); try { book.setStatus(BookStatus.fromCode(values.get("status"))); } catch (IllegalArgumentException ex) { errors.put("status", "Select a status."); } return new BookForm(book, values, errors); } private Map formValues(HttpServletRequest request) { Map values = new LinkedHashMap<>(); values.put("id", trim(request.getParameter("id"))); values.put("identifier", trim(request.getParameter("identifier"))); values.put("title", trim(request.getParameter("title"))); values.put("author", trim(request.getParameter("author"))); values.put("categoryId", trim(request.getParameter("categoryId"))); values.put("totalCopies", trim(request.getParameter("totalCopies"))); values.put("availableCopies", trim(request.getParameter("availableCopies"))); values.put("status", trim(request.getParameter("status"))); return values; } private BookSearchCriteria searchCriteria(HttpServletRequest request) { return new BookSearchCriteria( request.getParameter("identifier"), request.getParameter("title"), request.getParameter("author"), optionalLong(request.getParameter("categoryId")) ); } private Long optionalLong(String value) { String trimmed = trim(value); if (trimmed.isEmpty()) { return null; } try { return Long.valueOf(trimmed); } catch (NumberFormatException ex) { return -1L; } } private long parseLong(String value, String field, String message, Map errors) { String trimmed = trim(value); if (trimmed.isEmpty()) { errors.put(field, message); return 0L; } try { long parsed = Long.parseLong(trimmed); if (parsed <= 0) { errors.put(field, message); } return parsed; } catch (NumberFormatException ex) { errors.put(field, message); return 0L; } } private int parseInt(String value, String field, String message, Map errors) { String trimmed = trim(value); if (trimmed.isEmpty()) { errors.put(field, message); return -1; } try { return Integer.parseInt(trimmed); } catch (NumberFormatException ex) { errors.put(field, message); return -1; } } private long requiredLong(String value, long fallback) { try { long parsed = Long.parseLong(trim(value)); return parsed > 0 ? parsed : fallback; } catch (NumberFormatException ex) { return fallback; } } private boolean isPermissionDenied(ServiceResult result) { return !result.isSuccessful() && "You do not have permission to manage books.".equals(result.getMessage()); } private void forwardDenied(HttpServletRequest request, HttpServletResponse response, String message) throws ServletException, IOException { response.setStatus(HttpServletResponse.SC_FORBIDDEN); request.setAttribute("errorMessage", message); request.getRequestDispatcher(UNAUTHORIZED_JSP).forward(request, response); } private AuthenticatedUser currentUser(HttpServletRequest request) { HttpSession session = request.getSession(false); Object value = session == null ? null : session.getAttribute(SessionAttributes.AUTHENTICATED_USER); return value instanceof AuthenticatedUser ? (AuthenticatedUser) value : null; } private void applyFlash(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return; } moveFlash(session, request, FLASH_SUCCESS, "successMessage"); moveFlash(session, request, FLASH_ERROR, "errorMessage"); } private void moveFlash(HttpSession session, HttpServletRequest request, String sessionKey, String requestKey) { Object value = session.getAttribute(sessionKey); if (value != null) { request.setAttribute(requestKey, value); session.removeAttribute(sessionKey); } } private void flashSuccess(HttpServletRequest request, String message) { request.getSession().setAttribute(FLASH_SUCCESS, message); } private void flashError(HttpServletRequest request, String message) { request.getSession().setAttribute(FLASH_ERROR, message); } private String trim(String value) { return value == null ? "" : value.trim(); } private static final class BookForm { private final Book book; private final Map values; private final Map errors; private BookForm(Book book, Map values, Map errors) { this.book = book; this.values = values; this.errors = errors; } private Book getBook() { return book; } private Map getValues() { return values; } private Map getErrors() { return errors; } } }