• Daniel Byun

  • about

    projects

    contact me

simplified reddit home page clone

java logo mysql logo

This web app was created using Java EE, MySQL, JDBC, Servlet, and JSP was used for the view layer.

The goal of this web app is to clone the reddit home page.

features include:

  1. 1) login // register users
  2. 2) create // edit (only for authors) // reply (only for registered users) // view // delete boards

project directory:

java EE project directory

MySQL query: (create members)


 CREATE TABLE `members` (
     `id` int(11) NOT NULL AUTO_INCREMENT,
     `userId` varchar(100) NOT NULL,
     `userPw` varchar(100) NOT NULL,
     `userEmail` varchar(100) NOT NULL,
     PRIMARY KEY (`id`)
 )
                                    

MySQL query: (create boards)


 CREATE TABLE `posts` (
     `mId` int(11) NOT NULL AUTO_INCREMENT,
     `mGroup` int(11) NOT NULL,
     `step` int(11) NOT NULL,
     `hit` int(11) NOT NULL DEFAULT '0',
     `indent` int(11) NOT NULL,
     `userId` varchar(100) NOT NULL,
     `title` varchar(100) NOT NULL DEFAULT 'title',
     `content` varchar(400) NOT NULL,
     `date` timestamp NOT NULL,
     PRIMARY KEY (`mId`)
 )
                                    

database table looks like this:

database table

lets get to it!!!

context.xml


 // make sure the name matches the context lookup string in the DBConnect class

 <?xml version="1.0" encoding="UTF-8"?>
 <Context path="/">
    <Resource name="jdbc/db"
              auth="Container"
              type="javax.sql.DataSource"
              username="[]"
              password="[]"
              driverClassName="com.mysql.cj.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/webprojects?allowPublicKeyRetrieval=true&#038;useSSL=false&#038;amp&#038;useUnicode=true&#038;amp&#038;serverTimezone=UTC"
              loginTimeout="100"
              maxActive="500"
              maxIdle="100"
    />
 </Context>
                                    

web.xml


 <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    <!-- mysql -->
    <context-param>
        <description>sql driver</description>
        <param-name>driver</param-name>
        <param-value>com.mysql.cj.jdbc.Driver</param-value>
    </context-param>
    <context-param>
        <description>sql url</description>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/webprojects?allowPublicKeyRetrieval=true&amp;useSSL=false&amp;useUnicode=true&amp;serverTimezone=UTC</param-value>
    </context-param>
    <context-param>
        <description>hostId</description>
        <param-name>hostId</param-name>
        <param-value>[]</param-value>
    </context-param>
    <context-param>
        <description>hostPw</description>
        <param-name>hostPw</param-name>
        <param-value>[]</param-value>
    </context-param>

    <!-- connection pool -->
    <resource-ref>
        <description>connectDBCP</description>
        <res-ref-name>jdbc/db</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

    <!-- welcome files -->
    <display-name>simple reddit clone</display-name>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <!-- error page -->
    <error-page>
        <location>/error.html</location>
    </error-page>
    </web-app>
                                    

let's setup the connection with the database by setting up DBConnect (connection pooling setup)


 // this class links datasource information in the context.xml file and creates a dataSource
 package org.web.dbConnect;

 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.sql.DataSource;
 import java.sql.Connection;

 public class DBConnect {
     public static Connection getConnection() {
         Connection conn = null;
         try {
             Context initContext = new InitialContext();
             Context envContext = (Context) initContext.lookup("java:/comp/env");
             DataSource ds = (DataSource) envContext.lookup("jdbc/db");
             conn = ds.getConnection();
         } catch (Exception e) {
             e.printStackTrace();
         }

         return conn;
     }
 }
                                    

MemberDTO


 package org.web.memberDTO;

 public class MemberDTO {
     private int id;
     private String userId, userPw, userEmail;

     public MemberDTO(String userId, String userPw, String userEmail) {
         this.userId = userId;
         this.userPw = userPw;
         this.userEmail = userEmail;
     }

     public MemberDTO(int id, String userId, String userPw, String userEmail) {
         this.id = id;
         this.userId = userId;
         this.userPw = userPw;
         this.userEmail = userEmail;
     }

     public int getId() {
         return id;
     }

     public void setId(int id) {
         this.id = id;
     }

     public String getUserId() {
         return userId;
     }

     public void setUserId(String userId) {
         this.userId = userId;
     }

     public String getUserPw() {
         return userPw;
     }

     public void setUserPw(String userPw) {
         this.userPw = userPw;
     }

     public String getUserEmail() {
         return userEmail;
     }

     public void setUserEmail(String userEmail) {
         this.userEmail = userEmail;
     }

     // for debugging purposes
     @Override
     public String toString() {
         return "MemberDTO{" +
                 "id=" + id +
                 ", userId='" + userId + '\'' +
                 ", userPw='" + userPw + '\'' +
                 ", userEmail='" + userEmail + '\'' +
                 '}';
     }
 }
                                    

BoardDTO


 package org.web.boardDTO;

 import java.sql.Timestamp;

 public class BoardDTO {
     private int mId, mGroup, step, hit, indent;
     private String userName, title, content;
     private Timestamp date;

     public BoardDTO(int mId, int mGroup, int step, int hit, int indent, String userName, String title, String content, Timestamp date) {
         this.mId = mId;
         this.mGroup = mGroup;
         this.step = step;
         this.hit = hit;
         this.indent = indent;
         this.userName = userName;
         this.title = title;
         this.content = content;
         this.date = date;
     }

     public int getmId() {
         return mId;
     }

     public void setmId(int mId) {
         this.mId = mId;
     }

     public int getmGroup() {
         return mGroup;
     }

     public void setmGroup(int mGroup) {
         this.mGroup = mGroup;
     }

     public int getStep() {
         return step;
     }

     public void setStep(int step) {
         this.step = step;
     }

     public int getHit() {
         return hit;
     }

     public void setHit(int hit) {
         this.hit = hit;
     }

     public int getIndent() {
         return indent;
     }

     public void setIndent(int indent) {
         this.indent = indent;
     }

     public String getUserName() {
         return userName;
     }

     public void setUserName(String userName) {
         this.userName = userName;
     }

     public String getTitle() {
         return title;
     }

     public void setTitle(String title) {
         this.title = title;
     }

     public String getContent() {
         return content;
     }

     public void setContent(String content) {
         this.content = content;
     }

     public Timestamp getDate() {
         return date;
     }

     public void setDate(Timestamp date) {
         this.date = date;
     }
 }

                                    

Controller (redirecting users so they can't access the pages manually)


 package org.web.controller;

 import javax.servlet.RequestDispatcher;
 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 java.io.IOException;

 @WebServlet("*.do")
 public class Controller extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         doService(req, resp);
     }

     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         doService(req, resp);
     }

     private void doService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         resp.setCharacterEncoding("utf-8");

         // retrieving the url
         String path = req.getContextPath();
         String URI = req.getRequestURI();

         // substringing to cut out everything before /
         String basicURL = URI.substring(path.length());

         String url = "";

         // redirect links
         switch (basicURL) {
             case "/join.do":
                 url = "/join.jsp";

                 break;
             case "/login.do":
                 url = "/login.jsp";

                 break;
             case "/profile.do":
                 url = "/profile.jsp";

                 break;
             case "/updateProfile.do":
                 url = "/updateProfile.jsp";

                 break;
             case "/deleteProfile.do":
                 url = "/deleteProfile.jsp";

                 break;
             case "/profileDeleted.do":
                 url = "/profileDeleted.jsp";

                 break;
         }

         RequestDispatcher dispatcher = req.getRequestDispatcher(url);
         dispatcher.forward(req, resp);
     }
 }

                                    

MemberController


 package org.web.memberController;

 import org.web.memberCommand.*;

 import javax.servlet.RequestDispatcher;
 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 java.io.IOException;

 @WebServlet("*.mo")
 public class MemberController extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         moService(req, resp);
     }

     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         moService(req, resp);
     }

     private void moService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         resp.setCharacterEncoding("utf-8");

         String path = req.getContextPath();
         String URI = req.getRequestURI();
         String basicURL = URI.substring(path.length());

         String url = "";
         MemberCommand action;
         RequestDispatcher requestDispatcher;

         switch (basicURL) {
             case "/idCheck.mo":
                 action = new IdCheck();
                 action.executeQueryCommand(req, resp);

                 break;
             case "/join.mo":
                 action = new MemberJoin();
                 action.executeQueryCommand(req, resp);

                 url = (String) req.getAttribute("url");
                 requestDispatcher = req.getRequestDispatcher(url);
                 requestDispatcher.forward(req, resp);

                 break;
             case "/login.mo":
                 action = new MemberLogin();
                 action.executeQueryCommand(req, resp);

                 break;
             case "/logout.mo":
                 action = new MemberLogout();
                 action.executeQueryCommand(req, resp);

                 url = (String) req.getAttribute("url");
                 requestDispatcher = req.getRequestDispatcher(url);
                 requestDispatcher.forward(req, resp);

                 break;
             case "/viewProfile.mo":
                 action = new MemberView();
                 action.executeQueryCommand(req, resp);

                 url = (String) req.getAttribute("url");
                 requestDispatcher = req.getRequestDispatcher(url);
                 requestDispatcher.forward(req, resp);

                 break;
             case "/updateProfile.mo":
                 action = new MemberUpdate();
                 action.executeQueryCommand(req, resp);

                 url = (String) req.getAttribute("url");
                 requestDispatcher = req.getRequestDispatcher(url);
                 requestDispatcher.forward(req, resp);

                 break;
             case "/deleteProfile.mo":
                 action = new MemberDelete();
                 action.executeQueryCommand(req, resp);

                 url = (String) req.getAttribute("url");
                 requestDispatcher = req.getRequestDispatcher(url);
                 requestDispatcher.forward(req, resp);

                 break;
         }
     }
 }
                                    

BoardController


 package org.web.boardController;

 import org.web.boardCommand.*;

 import javax.servlet.RequestDispatcher;
 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 java.io.IOException;

 @WebServlet("*.bo")
 public class BoardController extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         doService(req, resp);
     }

     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         doService(req, resp);
     }

     private void doService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         req.setCharacterEncoding("utf8");

         String path = req.getContextPath();
         String uri = req.getRequestURI();
         String basicurl = uri.substring(path.length());

         String url = "";
         BoardCommand action;

         RequestDispatcher dispatcher;

         switch (basicurl) {
             case "/boardList.bo":
                 action = new BoardList();
                 action.executeQueryCommand(req, resp);
                 url = (String) req.getAttribute("url");

                 dispatcher = req.getRequestDispatcher(url);
                 dispatcher.forward(req, resp);

                 break;
             case"/boardCommentCount.bo":
                 action = new BoardCommentCount();
                 action.executeQueryCommand(req, resp);

                 break;
             case "/boardUpdate.bo":
                 action = new BoardUpdate();
                 action.executeQueryCommand(req, resp);

                 break;
             case "/boardWrite.bo":
                 action = new BoardWrite();
                 action.executeQueryCommand(req, resp);

                 break;
             case "/boardView.bo":
                 action = new BoardView();
                 action.executeQueryCommand(req, resp);
                 url = (String) req.getAttribute("url");

                 dispatcher = req.getRequestDispatcher(url);
                 dispatcher.forward(req, resp);

                 break;
             case "/boardReply.bo":
                 action = new BoardReply();
                 action.executeQueryCommand(req, resp);

                 break;
         }
     }
 }
                                    

interface command that will have a method for other operations to override


 package org.web.memberCommand;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;

 public interface MemberCommand {
     void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
 }
                                    

MemberCommand (login, logout, join, delete, update, view)

MemberLogin


 package org.web.memberCommand;

 import org.web.memberDAO.MemberDAO;
 import org.web.memberDTO.MemberDTO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class MemberLogin implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String userId = req.getParameter("userId");
         String userPw = req.getParameter("userPw");

         MemberDAO dao = MemberDAO.getInstance();
         PrintWriter out = resp.getWriter();

         HttpSession session = req.getSession(false);
         if (session != null && !session.isNew()) {
             session.invalidate();
         }

         int result = dao.memberLogin(userId, userPw);
         MemberDTO member = dao.memberLoginAndRetrieveAll(userId, userPw);

         if (result == 1) {
             session = req.getSession(true);

             session.setAttribute("sessionId", member.getUserId());
             session.setAttribute("member", member);
             session.setMaxInactiveInterval(60 * 10);
         }

         out.write(result + "");
         out.close();
     }
 }

                                    

MemberLogout


 package org.web.memberCommand;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;

 public class MemberLogout implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         HttpSession session = req.getSession();
         String url;

         if (session != null) {
             // terminate session
             session.invalidate();

             url = "/boardList.do";
         } else {
             url = "/logout.mo";
         }

         req.setAttribute("url", url);
     }
 }
                                    

MemberJoin


 package org.web.memberCommand;

 import org.web.memberDAO.MemberDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;

 public class MemberJoin implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         System.out.println("join command");

         String userId = req.getParameter("userId");
         String userPw = req.getParameter("userPw");
         String userEmail = req.getParameter("userEmail");

         MemberDAO dao = MemberDAO.getInstance();

         int result = dao.memberJoin(userId, userPw, userEmail);
         String url = "";

         if (result == 1) {
             url = "/login.jsp";
         } else {
             url = "/join.jsp";
         }

         // redirecting user
         req.setAttribute("url", url);
     }
 }
                                    

MemberView


 package org.web.memberCommand;

 import org.web.memberDAO.MemberDAO;
 import org.web.memberDTO.MemberDTO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;

 public class MemberView implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String memberId = req.getParameter("memberId");
         MemberDAO dao = MemberDAO.getInstance();
         MemberDTO member = dao.memberView(memberId);

         String url = "";

         if (member != null) {
             req.setAttribute("member", member);
             url = "/profile.do";
         } else {
             url = "/profile.do";
         }
         req.setAttribute("url", url);
     }
 }
                                    

MemberDelete


 package org.web.memberCommand;

 import org.web.memberDAO.MemberDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class MemberDelete implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String id = req.getParameter("id");

         String userId = req.getParameter("userId");
         String userPw = req.getParameter("userPw");

         MemberDAO dao = MemberDAO.getInstance();
         int result = dao.memberDelete(userId, userPw);

         HttpSession session = req.getSession();
         PrintWriter out = resp.getWriter();

         if (result == 1) {
             session.invalidate();
         }

         out.write(result + "");
         out.close();
     }
 }
                                    

MemberUpdate


 package org.web.memberCommand;

 import org.web.memberDAO.MemberDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;

 public class MemberUpdate implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         int id = Integer.parseInt(req.getParameter("memberId"));
         String userId = req.getParameter("userId");
         String userPw = req.getParameter("userPw");
         String userEmail = req.getParameter("userEmail");

         MemberDAO dao = MemberDAO.getInstance();
         int result = dao.memberUpdate(id, userId, userPw, userEmail);
         String url = "";

         // try to update session as well
         if (result == 1) {
             HttpSession session = req.getSession(false);
             session.getAttribute("sessionId");

             // session.setAttribute("member", member);

             url = "/viewProfile.mo?memberId=" + id;
             req.setAttribute("url", url);
         }
     }
 }

                                    

BoardCommand


 package org.web.boardCommand;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;

 public interface BoardCommand {
     void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
 }

                                    

BoardList


 package org.web.boardCommand;

 import org.web.boardDAO.BoardDAO;
 import org.web.boardDTO.BoardDTO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;

 public class BoardList implements BoardCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         BoardDAO dao = BoardDAO.getInstance();
         ArrayList<BoardDTO> lists = dao.list();

         req.setAttribute("lists", lists);
         req.setAttribute("url", "/boardList.jsp");
     }
 }

                                    

BoardView


 package org.web.boardCommand;

 import org.web.boardDAO.BoardDAO;
 import org.web.boardDTO.BoardDTO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.ArrayList;

 public class BoardView implements BoardCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         int mId = (Integer.parseInt(req.getParameter("mId")));

         BoardDAO dao = BoardDAO.getInstance();
         BoardDTO dto = dao.boardView(mId);

         // get the arraylist of all posts
         ArrayList<BoardDTO> lists = dao.list();

         // send all posts to boardView page
         req.setAttribute("lists", lists);

         // send the specific post requested to view
         req.setAttribute("list", dto);

         // req.setAttribute("count", count);
         req.setAttribute("url", "/boardView.jsp");
     }
 }
                                    

BoardWrite


 package org.web.boardCommand;

 import org.web.boardDAO.BoardDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class BoardWrite implements BoardCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String userId = req.getParameter("userId");
         String title = req.getParameter("title");
         String content = req.getParameter("content");

         BoardDAO dao = BoardDAO.getInstance();

         int result = dao.write(userId, title, content);
         PrintWriter out = resp.getWriter();

         out.write(result + "");
         out.close();
     }
 }

                                    

BoardDelete


 package org.web.boardCommand;

 import org.web.boardDAO.BoardDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class BoardDelete implements BoardCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String mId = req.getParameter("mId");

         BoardDAO dao = BoardDAO.getInstance();
         int result = dao.boardDelete(mId);
         PrintWriter out = resp.getWriter();

         if (result == 1) {
             out.write("1");
         } else {
             out.write("0");
         }

         out.close();
     }
 }

                                    

BoardUpdate


 package org.web.boardCommand;

 import org.web.boardDAO.BoardDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class BoardUpdate implements BoardCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         int mId = Integer.parseInt(req.getParameter("mId"));
         String title = req.getParameter("title");
         String content = req.getParameter("content");

         BoardDAO dao = BoardDAO.getInstance();
         int result = dao.boardUpdate(mId, title, content);

         PrintWriter out = resp.getWriter();

         out.write(result + "");
         out.close();
     }
 }
                                    

BoardReply


 package org.web.boardCommand;

 import org.web.boardDAO.BoardDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class BoardReply implements BoardCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         int mId = Integer.parseInt(req.getParameter("mId"));
         int mGroup = Integer.parseInt(req.getParameter("mGroup"));
         int step = Integer.parseInt(req.getParameter("step"));
         int indent = Integer.parseInt(req.getParameter("indent"));
         String userId = req.getParameter("userId");
         String title = req.getParameter("title");
         String content = req.getParameter("content");

         BoardDAO dao = BoardDAO.getInstance();
         int result = dao.boardReply(mId, mGroup, step, indent, userId, title, content);

         PrintWriter out = resp.getWriter();

         out.write(result + "");
         out.close();
     }
 }
                                    

MemberDAO (with Singleton pattern)


 package org.web.memberDAO;

 import org.web.dbConnect.DBConnect;
 import org.web.memberDTO.MemberDTO;

 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;

 public class MemberDAO {
     private Connection conn = null;
     private PreparedStatement pstm = null;
     private ResultSet rs = null;

     // singleton method to have MemberDAO be retrieved without starting a new instantiation every time
     private MemberDAO() {
     }

     public static MemberDAO getInstance() {
         return singleton.instance;
     }

     private static class singleton {
         private static final MemberDAO instance = new MemberDAO();
     }

     //  i put the last method in a method to make the code DRY
     private void cleanUp() {
         try {
             if (conn != null) conn.close();
             if (pstm != null) pstm.close();
             if (rs != null) rs.close();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }

     // id validation method
     public int idCheck(String userId) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "select count(*) from simpleDatabase.members where userId = ?";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, userId);
             rs = pstm.executeQuery();

             if (rs != null) {
                 while (rs.next()) {
                     result = rs.getInt(1);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     // join method
     public int memberJoin(String userId, String userPw, String userEmail) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "insert into simpleDatabase.members (id, userId, userPw, userEmail) values (null, ?, ?, ?)";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, userId);
             pstm.setString(2, userPw);
             pstm.setString(3, userEmail);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     public MemberDTO memberLoginAndRetrieveAll(String userId, String userPw) {
         MemberDTO member = null;

         try {
             conn = DBConnect.getConnection();
             String query = "select * from simpleDatabase.members where userId=? and userPw=?";
             pstm = conn.prepareStatement(query);
             pstm.setString(1, userId);
             pstm.setString(2, userPw);

             rs = pstm.executeQuery();

             if (rs != null) {
                 while (rs.next()) {
                     int memberId = rs.getInt(1);
                     String userId1 = rs.getString(2);
                     String userPw1 = rs.getString(3);
                     String userEmail = rs.getString(4);

                     member = new MemberDTO(memberId, userId1, userPw1, userEmail);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }
         return member;
     }

     public int memberLogin(String userId, String userPw) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "select count(*) from simpleDatabase.members where userId=? and userPw=?";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, userId);
             pstm.setString(2, userPw);

             rs = pstm.executeQuery();
             if (rs != null) {
                 while (rs.next()) {
                     result = rs.getInt(1);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     public int memberDelete(String userId, String userPw) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "delete from simpleDatabase.members where userId=? and userPw=?";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, userId);
             pstm.setString(2, userPw);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     public MemberDTO memberView(String memberId) {
         MemberDTO member = null;

         try {
             conn = DBConnect.getConnection();
             // String query = "select * from simpleDatabase.members where memberId=?";
             String query = "select * from simpleDatabase.members where id = ?";
             pstm = conn.prepareStatement(query);
             pstm.setString(1, memberId);

             rs = pstm.executeQuery();

             if (rs != null) {
                 while (rs.next()) {
                     int memberId1 = rs.getInt(1);
                     String userId = rs.getString(2);
                     String userPw = rs.getString(3);
                     String userEmail = rs.getString(4);

                     member = new MemberDTO(memberId1, userId, userPw, userEmail);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }
         return member;
     }

     public int memberUpdate(int id, String userId, String userPw, String userEmail) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "update simpleDatabase.members set userId=?, userPw=?, userEmail=? where id=?";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, userId);
             pstm.setString(2, userPw);
             pstm.setString(3, userEmail);
             pstm.setInt(4, id);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }
 }

                                    

BoardDAO


 package org.web.boardDAO;

 import org.web.boardDTO.BoardDTO;
 import org.web.dbConnect.DBConnect;

 import java.sql.*;
 import java.util.ArrayList;

 public class BoardDAO {
     private Connection conn = null;
     private PreparedStatement pstm = null;
     private ResultSet rs = null;

     private BoardDAO() {
     }

     // singleton pattern
     private static class singleton {
         private static final BoardDAO instance = new BoardDAO();
     }

     public static BoardDAO getInstance() {
         return singleton.instance;
     }

     private void cleanUp() {
         try {
             if (conn != null) {
                 conn.close();
             }
             if (pstm != null) {
                 pstm.close();
             }
             if (rs != null) {
                 rs.close();
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }
     }

     // list all boards method
     public ArrayList<BoardDTO> list() {
         ArrayList<BoardDTO> lists = new ArrayList<>();

         try {
             conn = DBConnect.getConnection();
             String query = "select * from simpleDatabase.posts order by hit desc, mGroup asc, indent asc";
             pstm = conn.prepareStatement(query);
             rs = pstm.executeQuery();

             if (rs != null) {
                 while (rs.next()) {
                     int mId = rs.getInt(1);
                     int mGroup = rs.getInt(2);
                     int mIndent = rs.getInt(3);
                     int step = rs.getInt(4);
                     int hit = rs.getInt(5);
                     String userId = rs.getString(6);
                     String title = rs.getString(7);
                     String content = rs.getString(8);
                     Timestamp date = rs.getTimestamp(9);

                     BoardDTO dto = new BoardDTO(mId, mGroup, mIndent, step, hit, userId, title, content, date);
                     lists.add(dto);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return lists;
     }

     public int write(String userId, String title, String content) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "insert into simpleDatabase.posts(mGroup, step, hit, indent, userId, title, content, date) values ((select case count(*) when 0 then 1 else max(mGroup) + 1 end from simpleDatabase.posts b1), 0, 0, 0, ?, ?, ?, now())";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, userId);
             pstm.setString(2, title);
             pstm.setString(3, content);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     public BoardDTO boardView(int mId) {
         BoardDTO dto = null;

         upHit(mId);

         try {
             conn = DBConnect.getConnection();
             String query = "select * from simpleDatabase.posts where mId=?";
             pstm = conn.prepareStatement(query);
             pstm.setInt(1, mId);

             rs = pstm.executeQuery();
             if (rs != null) {
                 while (rs.next()) {
                     int mId1 = rs.getInt(1);
                     int mGroup = rs.getInt(2);
                     int step = rs.getInt(3);
                     int hit = rs.getInt(4);
                     int indent = rs.getInt(5);
                     String userId = rs.getString(6);
                     String title = rs.getString(7);
                     String content = rs.getString(8);
                     Timestamp date = rs.getTimestamp(9);

                     dto = new BoardDTO(mId1, mGroup, step, hit, indent, userId, title, content, date);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return dto;
     }

     public int boardReply(int mId, int mGroup, int step, int indent, String userId, String title, String content) {
         int result = 0;

         boardReplyUpdate(mGroup, step);

         try {
             conn = DBConnect.getConnection();
             String query = "insert into simpleDatabase.posts(mId, mGroup, step, indent, userId, title, content, date) VALUES (null, ?, ?, ?, ?, ?, ?, now())";
             pstm = conn.prepareStatement(query);

             pstm.setInt(1, mGroup);
             pstm.setInt(2, step + 1);
             pstm.setInt(3, indent + 1);
             pstm.setString(4, userId);
             pstm.setString(5, title);
             pstm.setString(6, content);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     private int upHit(int mId) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "update simpleDatabase.posts set hit = hit + 1 where mId = ?";

             pstm = conn.prepareStatement(query);
             pstm.setInt(1, mId);
             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     private int boardReplyUpdate(int mGroup, int step) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "update simpleDatabase.posts set step = step + 1 where mGroup = ? and step > ?";
             pstm = conn.prepareStatement(query);

             pstm.setInt(1, mGroup);
             pstm.setInt(2, step);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     public int commentCount(int mGroup) {
         int count = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "select count(*) from simpleDatabase.posts where mGroup = ?";
             pstm = conn.prepareStatement(query);

             pstm.setInt(1, mGroup);

             rs = pstm.executeQuery();
             if (rs != null) {
                 while (rs.next()) {
                     count = rs.getInt(1);
                 }
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return count;
     }

     public int boardUpdate(int mId, String title, String content) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "update simpleDatabase.posts set title = ?, content = ? where mId = ?";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, title);
             pstm.setString(2, content);
             pstm.setInt(3, mId);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }

     public int boardDelete(String mId) {
         int result = 0;

         try {
             conn = DBConnect.getConnection();
             String query = "delete from simpleDatabase.posts where mId = ?";
             pstm = conn.prepareStatement(query);

             pstm.setString(1, mId);

             result = pstm.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             cleanUp();
         }

         return result;
     }
 }
                                    

IdCheck (to check if the id exists already in the database)


 package org.web.memberCommand;

 import org.web.memberDAO.MemberDAO;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;

 public class IdCheck implements MemberCommand {
     @Override
     public void executeQueryCommand(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String userId = req.getParameter("userId");
         MemberDAO dao = MemberDAO.getInstance();

         int result = dao.idCheck(userId);
         PrintWriter out = resp.getWriter();

         out.write(result + "");
         out.close();
     }
 }
                                    

check out part 2 for the view layer

simplified reddit home page clone part 2 (view layer)

views (using JSP)

index


 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <%
     response.sendRedirect("boardList.bo");
 %>
                                    

header page (to be included in other pages)


 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <header>
     <nav>
         <ul>
             <c:choose>
                 <c:when test="${sessionScope.sessionId != null}">
                     <li><a href="boardList.bo"><img src="img/spongebob%20face.jpg" alt="logo"></a></li>
                     <li><a href="boardList.bo">view posts</a></li>
                     <li><a href="boardWrite.jsp">write a post</a></li>
                     <li>
                         <div class="dropdown">
                             <span><a>${sessionScope.member.userId}</a></span>
                             <div class="dropdownContent">
                                 <a href="viewProfile.mo?memberId=${sessionScope.member.id}">my profile</a>
                                 <input type="text" hidden value="${sessionScope.memberId}" id="memberIdHeader">
                                 <a href="logout.mo">logout</a>
                             </div>
                         </div>
                     </li>
                 </c:when>
                 <c:otherwise>
                     <li><a href="boardList.bo"><img src="img/spongebob%20face.jpg" alt="logo"></a></li>
                     <li><a href="boardList.bo">view posts</a></li>
                     <li><a href="join.do">sign up</a></li>
                     <li><a href="login.do">login</a></li>
                 </c:otherwise>
             </c:choose>
         </ul>
     </nav>
 </header>
                                    

footer page


 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/core" %>
 <footer>
     <section>
         <jsp:useBean id="current" class="java.util.Date"/>
         <small>daniel byun &copy; ${(current.year) + 1900}</small>
     </section>
 </footer>
                                    

BoardList (main page)


 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <html>
     <head>
         <title>board list</title>
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/boardList.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
         <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
         <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section class="boardList">
                 <div class="boards">
                     <p style="color: white; text-align: center;">sorted by most clicked</p>
                     <c:forEach var="list" items="${lists}">
                         <c:choose>
                             <c:when test="${list.indent == 0}">
                                 <div class="boardWrap">
                                     <input type="text" name="indent" id="indent" hidden value="${list.indent}">
                                     <input type="text" name="mGroup" class="mGroup" hidden value="${list.mGroup}">
                                     <input type="text" name="content" id="content" hidden value="${list.content}">
                                     <a href="<c:url value="boardView.bo?mId=${list.mId}"/>">
                                         <div class="board">
                                             <jsp:useBean id="current" class="java.util.Date"/>
                                             <small>
                                                 posted by ${list.userName}
                                                 on ${list.date.toLocaleString()}
                                             </small>
                                             <h1>
                                                     ${list.title}
                                             </h1>
                                             <p class="content">
                                                     ${list.content}
                                             </p>
                                             <div class="social">
                                                 <a href="#" class="replyBtn">
                                                     <i class="fas fa-comment-alt"></i>
                                                     <c:choose>
                                                         <c:when test="${list.indent > 0}">
                                                             <c:choose>
                                                                 <c:when test="${list.indent == 1}">
                                                                     comment
                                                                 </c:when>
                                                                 <c:otherwise>
                                                                     comments
                                                                 </c:otherwise>
                                                             </c:choose>
                                                         </c:when>
                                                         <c:otherwise>
                                                             comment
                                                         </c:otherwise>
                                                     </c:choose>
                                                 </a>
                                                 <a href="#" class="tweetBtn">
                                                     <i class="fab fa-twitter"></i>
                                                     tweet
                                                 </a>
                                                 <a href="" class="favoriteBtn">
                                                     <i class="fas fa-star"></i>
                                                     favorite
                                                 </a>
                                                 <c:if test="${sessionScope.sessionId.equals(list.userName)}">
                                                     <a href="#" class="editBtn">
                                                         <i class="fas fa-pen-square"></i>
                                                         edit
                                                     </a>
                                                 </c:if>
                                             </div>
                                         </div>
                                         <div class="reply">
                                             <textarea name="replyBoard" id="replyBoard" cols="30" rows="10" placeholder="what are your thoughts?"></textarea>
                                             <input type="button" value="CANCEL" class="cancelBtn">
                                             <input type="button" value="REPLY" id="replySubmit">
                                         </div>
                                         <div class="editArea">
                                             <textarea name="editContent" id="editContent" cols="30" rows="10" placeholder="${list.content}"></textarea>
                                             <input type="button" value="CANCEL" class="cancelBtnEdit">
                                             <input type="button" value="EDIT" id="editSubmit">
                                         </div>
                                     </a>
                                 </div>
                             </c:when>
                         </c:choose>
                     </c:forEach>
                 </div>
                 <div class="page-load-status">
                     <div class="loader-ellips infinite-scroll-request">
                         <span class="loader-ellips__dot"></span>
                         <span class="loader-ellips__dot"></span>
                         <span class="loader-ellips__dot"></span>
                         <span class="loader-ellips__dot"></span>
                     </div>
                     <p class="infinite-scroll-last">End of content</p>
                     <p class="infinite-scroll-error">No more pages to load</p>
                 </div>
             </section>
         </main>
         <jsp:include page="footer.jsp"/>
         <script src="js/index.js"></script>
     </body>
 </html>
                                    

login page


 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <html>
     <head>
         <title>login</title>
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/login.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section>
                 <form action="login.mo" method="post" name="loginForm" id="loginForm">
                     <span id="errorMsg">error</span>
                     <ul>
                         <li><h1>login</h1></li>
                         <li><input type="text" name="userId" id="userId" placeholder="your id"></li>
                         <li><input type="password" name="userPw" id="userPw" placeholder="*****"></li>
                         <li><input type="button" value="login" id="loginBtn"></li>
                     </ul>
                 </form>
             </section>
         </main>
         <jsp:include page="footer.jsp"/>
         <script src="js/login.js"></script>
     </body>
 </html>
                                    

join page


 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <html>
     <head>
         <title>simple database</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/join.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section>
                 <form action="join.mo" method="post" name="joinForm" id="joinForm">
                     <span id="errorMsg">error</span>
                     <ul>
                         <li>
                             <input type="text" name="userId" id="userId" placeholder="your id">
                             <input type="hidden" name="idCheckOk" id="idCheckOk" value="idNotChecked">
                              <input type="button" value="check id" id="checkId">
                         </li>
                         <li>
                             <input type="password" name="userPw" id="userPw" placeholder="*****" onkeyup="checkPwFunc()">
                             <input type="password" name="userPwCheck" id="userPwCheck" placeholder="*****" onkeyup="checkPwFunc()">
                         </li>
                         <li>
                             <input type="email" name="userEmail" id="userEmail" placeholder="your email">
                         </li>
                         <li>
                             <input type="button" value="join" id="joinBtn">
                         </li>
                     </ul>
                 </form>
             </section>
         </main>
         <jsp:include page="footer.jsp"/>
         <script src="js/join.js"></script>
     </body>
 </html>
                                    

view member


 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <c:if test="${sessionScope.sessionId == null}">
     <script>
         alert("please log in to view this page!");
     </script>
     <c:redirect url="boardList.do"/>
 </c:if>
 <html>
     <head>
         <title>view my profile</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/profile.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section>
                 <form action="updateProfile.mo" id="updateForm">
                     <span id="errorMsg">error</span>
                     <ul>
                         <li><h1>${member.userId}'s profile</h1></li>
                         <li>
                             <input type="hidden" name="memberId" id="memberId" value="${member.id}">
                             <input type="text" name="userId" id="userId" placeholder="${member.userId}">
                             <input type="button" value="check id" id="checkId">
                             <input type="hidden" name="idCheckOk" id="idCheckOk" value="idNotChecked">
                         </li>
                         <li>
                             <input type="email" name="userEmail" id="userEmail" placeholder="${member.userEmail}">
                         </li>
                         <li>
                             <input type="password" name="userPw" id="userPw" placeholder="********" onkeyup="checkPwFunc()">
                             <input type="password" name="userPwCheck" id="userPwCheck" placeholder="********" onkeyup="checkPwFunc()">
                         </li>
                         <li><input type="button" value="click here to update your profile" id="updateBtn"></li>
                         <li><input type="button" value="click here to delete your profile" id="deleteRedirect"></li>
                     </ul>
                 </form>
             </section>
         </main>
         <jsp:include page="footer.jsp"/>
         <script src="js/update.js"></script>
     </body>
 </html>
                                    

delete member


 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <c:choose>
     <c:when test="${sessionScope.sessionId == null}">
         <script>
             alert("please log in to view this page!");
         </script>
         <c:redirect url="index.jsp"/>
     </c:when>
 </c:choose>
 <html>
     <head>
         <title>delete page</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/deleteProfile.css">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section>
                 <form action="deleteProfile.mo" method="post" id="deleteForm">
                     <span id="errorMsg"></span>
                     <ul>
                         <h1>delete your profile</h1>
                         <li>
                             <input type="text" name="userId" id="userId" placeholder="your id">
                         </li>
                         <li>
                             <input type="password" name="userPw" id="userPw" placeholder="your password">
                         </li>
                         <li>
                             <input type="button" value="delete" id="deleteBtn">
                         </li>
                     </ul>
                 </form>
             </section>
         </main>
         <jsp:include page="footer.jsp"/>
         <script src="js/delete.js"></script>
     </body>
 </html>
                                    

update profile


 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <c:if test="${sessionScope.sessionId == null}">
     <script>
         alert("please log in to view this page!");
     </script>
     <c:redirect url="boardList.do"/>
 </c:if>
 <html>
     <head>
         <title> update profile</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/updateProfile.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section>
                 <form action="updateProfile.mo" method="post">
                     <span id="errorMsg">error</span>
                     <ul>
                         <li>
                             <input type="text" name="userId" id="userId" placeholder="your new id">
                             <input type="hidden" name="" id=memberId" value="${member.memberId}">
                             <input type="hidden" name="" value="${member.userId}">
                             <input type="hidden" name="idCheckOk" id="idCheckOk" value="idNotChecked">
                             <input type="button" value="check id" id="checkId">
                         </li>
                         <li>
                             <input type="email" name="userEmail" id="userEmail" placeholder="your new email">
                         </li>
                         <li>
                             <input type="password" name="userPw" id="userPw" placeholder="*****" onkeyup="checkPwFunc()">
                             <input type="password" name="userPwCheck" id="userPwCheck" placeholder="*****" onkeyup="checkPwFunc()">
                         </li>
                         <li>
                             <input type="button" value="update" id="updateBtn">
                         </li>
                     </ul>
                 </form>
             </section>
         </main>
         <script src="js/update.js"></script>
         <jsp:include page="footer.jsp"/>
     </body>
 </html>
                                    

boardWrite page


 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
     <head>
         <title>board write</title>
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/boardWrite.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section class="writeSection">
                 <h1>
                     Create a post
                 </h1>
                 <form action="boardWrite.bo" method="post" id="writeForm">
                     <ul>
                         <li>
                             <span class="errorMsg"></span>
                         </li>
                         <li>
                             <input type="text" name="userId" id="userId" value="${sessionScope.member.userId}" hidden placeholder="${sessionScope.member.userId}">
                         </li>
                         <li>
                             <input type="text" name="title" id="title" placeholder="title">
                         </li>
                         <li>
                             <textarea name="content" id="content" cols="30" rows="9" placeholder="text"></textarea>
                         </li>
                         <li>
                             <input type="button" value="POST" id="writeBtn" disabled>
                         </li>
                     </ul>
                 </form>
             </section>
         </main>
         <script src="js/boardWrite.js"></script>
         <jsp:include page="footer.jsp"/>
     </body>
 </html>
                                    

board view page


 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <html>
     <head>
         <title>board view</title>
         <meta charset="utf-8">
         <link rel="stylesheet" href="css/reset.css">
         <link rel="stylesheet" href="css/boardView.css">
         <link rel="stylesheet" href="css/header.css">
         <link rel="stylesheet" href="css/footer.css">
         <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
     </head>
     <body>
         <jsp:include page="header.jsp"/>
         <main>
             <section class="boardView">
                 <div class="boards">
                     <div class="board" style="position: relative; left: ${list.indent}rem">
                         <jsp:useBean id="current" class="java.util.Date"/>
                         <div class="post">
                             <div class="attr">
                                 <input type="text" name="mId" id="mId" hidden value="${list.mId}">
                                 <input type="text" name="mGroup" id="mGroup" hidden value="${list.mGroup}">
                                 <input type="text" name="step" id="step" hidden value="${list.step}">
                                 <input type="text" name="indent" id="indent" hidden value="${list.indent}">
                                 <input type="text" name="userId" id="userId" hidden value="${sessionScope.member.userId}">
                                 <input type="text" name="title" id="title" hidden value="${list.title}">
                                 <input type="text" name="content" id="content" hidden value="${list.content}">
                             </div>
                             <small>
                                 posted by ${list.userName}
                                 on ${list.date.toLocaleString()}
                             </small>
                             <h1>
                                 ${list.title}
                             </h1>
                             <p class="content">
                                 ${list.content}
                             </p>
                             <c:choose>
                                 <c:when test="${list.indent > 0}">
                                     <c:choose>
                                         <c:when test="${list.indent == 1}">
                                             ${list.indent} comment
                                         </c:when>
                                         <c:otherwise>
                                             ${list.indent} comments
                                         </c:otherwise>
                                     </c:choose>
                                 </c:when>
                             </c:choose>
                             <div class="editArea">
                                 <textarea name="editContent" id="editContent" cols="30" rows="10" placeholder="${list.content}"></textarea>
                                 <input type="button" value="CANCEL" id="cancelBtn">
                                 <input type="button" value="EDIT" id="editBtn">
                             </div>
                         </div>
                         <c:choose>
                             <c:when test="${sessionScope.sessionId != null}">
                                 <ul class="commentPrompt">
                                     <li>
                                         <a href=""><i class="fas fa-comment-alt"></i>comments</a>
                                         <a href=""><i class="fas fa-star"></i>favorite</a>
                                         <a href=""><i class="fab fa-twitter"></i>tweet</a>
                                         <c:if test="${sessionScope.sessionId.equals(list.userName)}">
                                             <a href="#" id="edit"><i class="fas fa-pen-square"></i>edit</a>
                                         </c:if>
                                     </li>
                                 </ul>
                                 <ul class="reply">
                                     <li>
                                         <textarea name="reply" class="reply" cols="30" rows="10" placeholder="what are your thoughts?"></textarea>
                                         <input type="button" value="COMMENT" class="replyBtn">
                                     </li>
                                 </ul>
                             </c:when>
                             <c:otherwise>
                                 <ul class="signUpPrompt">
                                     <li>
                                         <p>What are your thoughts? Log in or Sign up</p>
                                     </li>
                                     <li>
                                         <a href="login.do">LOG IN</a>
                                         <a href="join.do">SIGN UP</a>
                                     </li>
                                 </ul>
                             </c:otherwise>
                         </c:choose>
                     </div>

                     <c:forEach var="lists" items="${lists}" varStatus="loop">
                         <c:if test="${list.mGroup == lists.mGroup && not loop.first && lists.step > 0}">
                             <section class="replyBoard" style="margin-left: ${lists.indent * 2}rem">
                                 <div class="post">
                                     <small>
                                         posted by ${lists.userName}
                                         on ${lists.date.toLocaleString()}
                                     </small>
                                     <h1>
                                             ${lists.title}
                                     </h1>
                                     <p class="content">
                                             ${lists.content}
                                     </p>
                                 </div>
                                 <ul class="commentPrompt">
                                     <li>
                                         <a href=""><i class="fas fa-comment-alt"></i>reply</a>
                                         <c:if test="${sessionScope.sessionId.equals(list.userName)}">
                                             <a href="#" id="edit"><i class="fas fa-pen-square"></i>edit</a>
                                         </c:if>
                                     </li>
                                 </ul>
                                 <ul class="reply">
                                     <li>
                                         <textarea name="reply" class="reply" cols="30" rows="10" placeholder="what are your thoughts?"></textarea>
                                         <input type="button" value="COMMENT" class="replyBtn">
                                     </li>
                                 </ul>
                             </section>
                         </c:if>
                     </c:forEach>
                 </div>
             </section>
         </main>
         <jsp:include page="footer.jsp"/>
         <script src="js/boardReply.js"></script>
     </body>
 </html>
                                    

error page


   <!DOCTYPE html>
 <html lang="en">
     <head>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>Title</title>
     </head>
     <body>
         <h1>
             aw schucks, something went wrong!
         </h1>
     </body>
 </html>
                                    

javascript files

index.js


 let mGroup = document.getElementsByClassName("mGroup");
 let mGroupArr = [];
 let replyBoard = document.getElementById("replyBoard");
 let editContent = document.getElementById("editContent");

 // retrieve each mGroup number from each board
 for (let i = 0; i < mGroup.length; i++) {
     mGroupArr.push(`${mGroup[i].value}`);
 }

 let url = "boardCommentCount.bo";
 let data = `mGroupArr=${mGroupArr}`;

 // jquery scroll detection v2 (feature not implemented yet)
 $(window).scroll(function () {
     if ($(window).scrollTop() + $(window).height() === getDocHeight()) {
         alert("bottom!");
     }

     // ajax call to retrieve more boards
     let url = "";
     let data = ``;

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then(data => function () {
             if (data == 1) {
                 console.log("successful");
             } else {
                 console.log("failed");
             }
         })
 });

 // get document height for different browsers, different machines
 function getDocHeight() {
     let D = document;

     return Math.max(
         D.body.scrollHeight, D.documentElement.scrollHeight,
         D.body.offsetHeight, D.documentElement.offsetHeight,
         D.body.clientHeight, D.documentElement.clientHeight
     );
 }

 // buttons
 let tweetBtn = document.getElementsByClassName("tweetBtn");
 let replyBtn = document.getElementsByClassName("replyBtn");
 let editBtn = document.getElementsByClassName("editBtn");
 let cancelBtn = document.getElementsByClassName("cancelBtn");
 let cancelBtnEdit = document.getElementsByClassName("cancelBtnEdit");
 let content = document.getElementsByClassName("content");
 let editSubmit = document.getElementById("editSubmit");

 // textareas
 let replyArea = document.getElementsByClassName("reply");
 let editArea = document.getElementsByClassName("editArea");

 // reply board showing?
 let replyShowing = false;
 // edit textarea showing?
 let editShowing = false;

 let boardWrap = $(".boardWrap");

 for (let j = 0; j <= boardWrap.length + 1; j++) {
     // tweet button click function
     tweetBtn[j].addEventListener("click", function () {
         window.open(`https://twitter.com/share?text=${content[j].textContent.trim()}`, "", "menubar=no, toolbar=no, resizable=yes, scrollbars=yes, height=550, width=420");
     });

     // toggle **cancel button should also do that
     replyBtn[j].addEventListener("click", function () {
         if (!replyShowing) {
             replyArea[j].style.display = "flex";
             editArea[j].style.display = "none";
         }
         replyShowing = true;
     });

     // textarea should disappear when the user clicks 'cancel'
     cancelBtn[j].addEventListener("click", function () {
         if (replyShowing) {
             replyArea[j].style.display = "none";
         }
         replyShowing = false;
     });

     cancelBtnEdit[j].addEventListener("click", function () {
         if (editShowing) {
             editArea[j].style.display = "none";
         }
         editContent.textContent = "";
         editShowing = false;
     });

     editBtn[j].addEventListener("click", function () {
         if (!editShowing) {
             editArea[j].style.display = "flex";
             replyArea[j].style.display = "none";

             editShowing = true;
         } else if (editShowing) {
             editArea[j].style.display = "none";

             editShowing = false;
         }
     });

     editContent[j].addEventListener("keyup", function () {
         if (editContent[j].value === null || editContent[j].value.trim() === "") {

             editSubmit[j].disabled = true;
             editSubmit[j].style.cursor = "not-allowed";

             return false;
         }

         editSubmit[j].disabled = false;
         editSubmit[j].style.cursor = "pointer";
         editSubmit[j].style.color = "black"
     });
 }
                                    

login


 // error message
 let errorMessage = document.getElementById("errorMsg");

 // login Btn
 let loginBtn = document.getElementById("loginBtn");
 loginBtn.addEventListener("click", loginOk);

 function loginOk() {
     let userId = document.getElementById("userId");
     let userPw = document.getElementById("userPw");

     if (userId.value === null || userId.value.trim() === "") {
         errorMessage.style.display = "flex";
         errorMessage.innerHTML = "please put in your id";

         userId.focus();
         return false;
     }
     if (userPw.value === null || userPw.value.trim() === "") {
         errorMessage.style.display = "flex";
         errorMessage.innerHTML = "please put in your pw";

         userPw.focus();

         return false;
     }

     // ajax (fetch API)
     let data = `userId=${encodeURIComponent(userId.value)}&userPw=${encodeURIComponent(userPw.value)}`;
     let url = "login.mo";

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
             // successful
             if (data == 1) {
                 location.href = "boardList.bo";
             } else {
                 errorMessage.style.display = "flex";
                 errorMessage.innerHTML = "something went wrong, please try again";
                 userId.focus();
             }
         })
 }
                                    

join


 // form
 let joinForm = document.getElementById("joinForm");

 // check id button
 let checkId = document.getElementById("checkId");

 // whether the user checked the id or not
 let checkIdConfirmation = document.getElementById("idCheckOk");

 // error message span on top
 let errorMessage = document.getElementById("errorMsg");

 // user credentials
 let userId = document.getElementById("userId");
 let userPw = document.getElementById("userPw");
 let userEmail = document.getElementById("userEmail");

 // check the passwords
 let checkPwCheck = document.getElementById("userPwCheck");

 // submit button
 let joinBtn = document.getElementById("joinBtn");

 // event listeners
 checkId.addEventListener("click", userIdCheck);
 joinBtn.addEventListener("click", joinOk);

 function isUserIdEmpty() {
     if (userId.value === null || userId.value.trim() === "") {
         errorMessage.style.display = "flex";
         errorMessage.innerHTML = "please put in your id to check!";

         userId.focus();

         return false;
     } else {
         return true;
     }
 }

 // created a function because it's used more than once.
 function userIdCheck() {
     // if its not empty
     if (isUserIdEmpty() === true) {
         checkID();
     }
 }

 // check if the two password field values match
 function checkPwFunc() {
     let userPw = document.getElementById("userPw");
     let userPwCheck = document.getElementById("userPwCheck");

     if (userPw.value !== userPwCheck.value) {
         errorMessage.style.backgroundColor = "red";
         errorMessage.innerHTML = "password doesn't match";
         return false;
     } else {
         errorMessage.innerHTML = "password matches!";
         errorMessage.style.backgroundColor = "cadetblue";
         return true;
     }
 }

 // check id in database if theres any duplicates
 function checkID() {
     let userId = document.getElementById("userId");
     let userPw = document.getElementById("userPw");
     let data = `userId=${encodeURIComponent(userId.value)}`;
     let url = "idCheck.mo";

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
             // this is where we retrieve the result
             if (data == 1) {
                 errorMessage.style.display = "flex";
                 errorMessage.innerHTML = "that id is taken, please try a different one!";

                 userId.focus();
             } else {
                 errorMessage.style.display = "flex";
                 errorMessage.style.backgroundColor = "cadetblue";
                 errorMessage.innerHTML = "good to go!";

                 userPw.focus();
             }
         });

     // change the value to the 'checked'
     checkIdConfirmation.value = "checked";
 }

 // join method
 function joinOk() {
     // userId field check
     if (isUserIdEmpty() === true) {
         // if userId have not been checked
         if (checkIdConfirmation.value !== "checked") {
             errorMessage.style.backgroundColor = "red";
             errorMessage.innerHTML = "please make sure you check the id";

             userId.focus();

             return false;
         }
         // if the userPw field is empty
         if (userPw.value === null || userPw.value.trim() === "") {
             errorMessage.style.display = "flex";
             errorMessage.innerHTML = "please put in your password";

             userPw.focus();

             return false;
         }
         // if the two password fields don't match
         if (userPw.value !== checkPwCheck.value) {
             errorMessage.style.display = "flex";
             errorMessage.innerHTML = "the passwords don't match, please try again";

             userPw.focus();

             return false;
         }
         // if the userEmail field is empty
         if (userEmail.value === null || userEmail.value.trim() === "") {
             errorMessage.style.display = "flex";
             errorMessage.innerHTML = "please put in your email";

             userEmail.focus();

             return false;
         }

         joinForm.submit();
     }
 }
                                    

delete


 // error message
 let errorMessage = document.getElementById("errorMsg");

 // delete btn
 let deleteBtn = document.getElementById("deleteBtn");
 deleteBtn.addEventListener("click", deleteOk);

 function deleteOk() {
     let userId = document.getElementById("userId");
     let userPw = document.getElementById("userPw");

     if (userId.value === null || userId.value.trim() === "") {
         errorMessage.style.display = "flex";
         errorMessage.innerHTML = "please put in your id";

         userId.focus();
         return false;
     }
     if (userPw.value === null || userPw.value.trim() === "") {
         errorMessage.style.display = "flex";
         errorMessage.innerHTML = "please put in your password";

         userPw.focus();
         return false;
     }

     // fetch data
     let data = `userId=${encodeURIComponent(userId.value)}&userPw=${encodeURIComponent(userPw.value)}`;
     let url = "deleteProfile.mo";

     fetch (`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
             // successful
             if (data == 1) {
                 location.href = "/boardList.do";
             } else {
                 location.href = "/profile.do";
             }
         })
 }
                                    

update


 let memberId = document.getElementById("memberId");
 let userId = document.getElementById("userId");
 let userEmail = document.getElementById("userEmail");
 let userPw = document.getElementById("userPw");
 let userPwCheck = document.getElementById("userPwCheck");
 let checkIdConfirmation = document.getElementById("idCheckOk");
 let updateForm = document.getElementById("updateForm");

 let deleteRedirect = document.getElementById("deleteRedirect");
 deleteRedirect.addEventListener("click", redirect);

 function redirect() {
     location.href = "deleteProfile.do";
 }

 let errorMsg = document.getElementById("errorMsg");
 let updateBtn = document.getElementById("updateBtn");
 updateBtn.addEventListener("click", updateFunc);

 let checkId = document.getElementById("checkId");
 checkId.addEventListener("click", userIdCheck);

 function isUserIdEmpty() {
     if (userId.value === null || userId.value.trim() === "") {
         errorMsg.style.display = "flex";
         errorMsg.innerHTML = "please put in your id to check!";

         userId.focus();

         return false;
     } else {
         return true;
     }
 }

 function userIdCheck() {
     // if its not empty
     if (isUserIdEmpty() === true) {
         checkID();
     }
 }

 function updateFunc() {
     if (isUserIdEmpty() === true) {
         if (checkIdConfirmation.value !== "checked") {
             errorMsg.style.backgroundColor = "red";
             errorMsg.innerHTML = "please make sure you check the id";

             userId.focus();

             return false;
         }
         if (userId.value === null || userId.value.trim() === "") {
             errorMsg.style.display = "flex";
             errorMsg.innerHTML = "please put in your new id";

             userId.focus();

             return false;
         }
         if (userEmail.value === null || userEmail.value.trim() === "") {
             errorMsg.style.display = "flex";
             errorMsg.innerHTML = "please put in your new email address";

             userEmail.focus();

             return false;
         }
         if (userPw.value === null || userPw.value.trim() === "") {
             errorMsg.style.display = "flex";
             errorMsg.innerHTML = "please put in your new password";

             userPw.focus();

             return false;
         }

         updateForm.submit();
     }
 }

 function checkID() {
     let data = `userId=${encodeURIComponent(userId.value)}`;
     let url = "idCheck.mo";

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
             // this is where we retrieve the result
             if (data == 1) {
                 errorMsg.style.display = "flex";
                 errorMsg.innerHTML = "that id is taken, please try a different one!";

                 userId.focus();
             } else {
                 errorMsg.style.display = "flex";
                 errorMsg.style.backgroundColor = "cadetblue";
                 errorMsg.innerHTML = "good to go!";

                 userEmail.focus();
             }
         });

     // change the value to the 'checked'
     checkIdConfirmation.value = "checked";
 }

 function checkPwFunc() {
     if (userPw.value !== userPwCheck.value) {
         errorMsg.style.backgroundColor = "red";
         errorMsg.innerHTML = "password doesn't match";
         return false;
     } else {
         errorMsg.innerHTML = "password matches!";
         errorMsg.style.backgroundColor = "cadetblue";
         return true;
     }
 }
                                    

boardWrite


 // html elements
 let userId = document.getElementById("userId");
 let title = document.getElementById("title");
 let content = document.getElementById("content");
 let writeBtn = document.getElementById("writeBtn");

 // trigger write ajax when button is clicked
 writeBtn.addEventListener("click", write);

 // disable button to start
 writeBtn.disabled = true;

 // condition to see if the user has filled in something (disables again if the user deletes the content)
 [title, content].forEach(function (element) {
     element.addEventListener("keyup", function () {
         if (title.value === null || title.value.trim() === "") {
             writeBtn.disabled = true;
             writeBtn.style.cursor = "not-allowed";

             return false;
         }

         if (content.value === null || content.value.trim() === "") {
             writeBtn.disabled = true;
             writeBtn.style.cursor = "not-allowed";

             return false;
         }
         writeBtn.disabled = false;
         writeBtn.style.cursor = "pointer";
         writeBtn.style.color = "black";
     });
 });

 // write ajax call (using fetch) to controller
 function write() {
     let url = "boardWrite.bo";
     let data = `userId=${encodeURIComponent(userId.value)}&title=${encodeURIComponent(title.value)}&content=${encodeURIComponent(content.value)}`;

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
             if (data == 1) {
                 location.href = "boardList.bo";
             } else {
                 alert("something went wrong, try again");

                 title.focus();
                 return false;
             }
         })
 }
                                    

boardReply


 /* general html elements */
 let mId = document.getElementById("mId");
 let mGroup = document.getElementById("mGroup");
 let step = document.getElementById("step");
 let indent = document.getElementById("indent");

 // let userId = document.getElementById("userId");
 let title = document.getElementById("title");
 let content = document.getElementById("content");

 /* REPLY SECTION */
 // html elements
 let replyText = document.getElementById("reply");
 let replyBtn = document.getElementById("replyBtn");

 // trigger ajax call when the button is clicked
 replyBtn.addEventListener("click", reply);

 // disable button until the textarea is filled
 replyBtn.disabled = true;

 replyText.addEventListener("keyup", function () {
     if (replyText.value === null || replyText.value.trim() === "") {
         replyBtn.disabled = true;
         replyBtn.style.cursor = "not-allowed";
         replyBtn.style.color = "rgb(215, 218, 220)";

         return false;
     }
     replyBtn.disabled = false;
     replyBtn.style.cursor = "pointer";
     replyBtn.style.color = "black";
 });

 // reply ajax call (using fetch) to controller
 function reply() {
     let url = "boardReply.bo";
     let data = `mId=${mId.value}&mGroup=${mGroup.value}&step=${step.value}&indent=${indent.value}&userId=${userId.value}&title=${`re: ` + title.value}&content=${replyText.value}`;

     console.log(`${url}?${data}`);

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
             if (data == 1) {
                 location.href = "boardList.bo";
             }
         })
 }

 /*=========================================================*/
 /* EDIT SECTION */
 // edit button
 let showEdit = document.getElementById("edit");
 let cancelBtn = document.getElementById("cancelBtn");
 let editContent = document.getElementById("editContent");

 let editBtn = document.getElementById("editBtn");
 editBtn.addEventListener("click", editContentFunc);

 // disable edit button
 editBtn.disabled = true;

 // edit div
 let editArea = document.getElementsByClassName("editArea");

 // edit trigger
 showEdit.addEventListener("click", editDisplay);
 cancelBtn.addEventListener("click", cancelFunc);

 // show edit section
 function editDisplay() {
     editArea[0].style.display = "flex";
 }

 function cancelFunc() {
     editArea[0].style.display = "none";
     editContent.value = "";
 }

 // edit content
 editContent.addEventListener("keyup", checkContent);

 function checkContent() {
     if (editContent.value === null || editContent.value.trim() === "") {
         editBtn.disabled = true;
         editBtn.style.cursor = "not-allowed";
         editBtn.style.color = "rgb(215, 218, 220)";

         return false;
     }
     editBtn.disabled = false;
     editBtn.style.cursor = "pointer";
     editBtn.style.color = "black";
 }

 // edit function
 // need mId, title (unchanged), content (new)
 function editContentFunc() {
     let url = "boardUpdate.bo";
     let data = `mId=${mId.value}&title=${title.value}&content=${editContent.value}`;

     console.log(`${url}?${data}`);

     fetch(`${url}?${data}`)
         .then((resp) => resp.text())
         .then((data) => {
                 if (data == 1) {
                     location.href = `boardView.bo?mId=${mId.value}`;
                 } else {
                     alert("failed, try again");
                 }
             }
         )
 }
                                    

home page (boardList)

home page

login page

login page

error during login

wrong login

home page (boardList) logged in * can edit the post they made

logged in home

sign up new user

sign up

id taken

id taken page

id not taken

id not taken page

password doesn't match

password doesn't match

password matches and good to proceed on sign up

password matches

user update

view profile

view profile

delete profile

delete profile

now let's write a post

write a post

write a post

write a post process

writing a post process

write a post success

writing a post success

view a specific board (when not logged in)

board view when not logged in

view a specific board (when logged in)

board view when logged in

reply process

board reply process

reply to a board

board reply successful


final thoughts

there are a lot of code to accomplish things that can be written with more efficiency.

For example, using Spring MVC, Spring Security, etc we can accomplish the similar things with more features with less lines of code.

there are areas in this project I would like to improve on, add onto, and make sure the features function properly.


  • 1) for instance, having a 'favorite' feature that functions like a 'bookmark' function. the user can 'favorite' a post and can access it in the user's 'favorited' page
  • 2) passwords should always be encrypted before being pushed out to production, right now in this project they are being saved as plaintext and also has no requirements before signing up.
  • 3) would like to implement a better sorting query so that the user can choose the sorting method (recent, most liked, etc)
  • 4) finish implementing the edit feature
  • 5) finish implement the reply feature on the main page

github link to project

register // login app that supports multi-role feature (part 1)

spring logo mysql logo

This web app was created using Spring MVC, Spring Security, MySQL, JPA (Hibernate), and JSP was used for the view layer.

no XML configuration was used, java configuration only.

github link to project

demo app

    try it with
  • [ADMIN] username: admin, password: admin
  • [MANAGER] username: manager, password: manager
  • [EMPLOYEE] username: employee, password: employee
  • or register your own!

project directory

project directory

project dependencies (pom.xml)

 <?xml version="1.0" encoding="UTF-8"?>

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>

     <groupId>com.danielbyun</groupId>
     <artifactId>spring-security-demo-11-registration-multiroles</artifactId>
     <version>1.0-SNAPSHOT</version>
     <packaging>war</packaging>

     <name>spring-security-demo-10-registration Maven Webapp</name>
     <!-- FIXME change it to the project's website -->
     <url>http://www.example.com</url>

     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.7</maven.compiler.source>
         <maven.compiler.target>1.7</maven.compiler.target>
         <springframework.version>5.1.1.RELEASE</springframework.version>
         <springsecurity.version>5.1.1.RELEASE</springsecurity.version>
         <hibernate.version>5.3.7.FINAL</hibernate.version>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
     </properties>

     <dependencies>
         <!-- Spring MVC support -->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-webmvc</artifactId>
             <version>${springframework.version}</version>
         </dependency>

         <!-- spring -->
         <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-web</artifactId>
             <version>${springsecurity.version}</version>
         </dependency>

         <!-- spring security -->
         <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-config</artifactId>
             <version>${springsecurity.version}</version>
         </dependency>

         <!-- add spring security taglibs suppport -->
         <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-taglibs</artifactId>
             <version>${springsecurity.version}</version>
         </dependency>

         <!-- spring transactions -->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-tx</artifactId>
             <version>${springframework.version}</version>
         </dependency>

         <!-- spring orm -->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-orm</artifactId>
             <version>${springframework.version}</version>
         </dependency>

         <!-- Servlet, JSP and JSTL support -->
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <version>3.1.0</version>
         </dependency>
         <dependency>
             <groupId>javax.servlet.jsp</groupId>
             <artifactId>javax.servlet.jsp-api</artifactId>
             <version>2.3.3</version>
         </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>jstl</artifactId>
             <version>1.2</version>
         </dependency>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.3.1</version>
             <scope>test</scope>
         </dependency>

         <!-- mysql and connection pooling -->
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>8.0.13</version>
         </dependency>
         <dependency>
             <groupId>com.mchange</groupId>
             <artifactId>c3p0</artifactId>
             <version>0.9.5.2</version>
             <scope>compile</scope>
         </dependency>

         <!-- hibernate core -->
         <dependency>
             <groupId>org.hibernate</groupId>
             <artifactId>hibernate-core</artifactId>
             <version>5.3.7.Final</version>
         </dependency>

         <!-- hibernate validator -->
         <dependency>
             <groupId>org.hibernate</groupId>
             <artifactId>hibernate-validator</artifactId>
             <version>6.0.13.Final</version>
         </dependency>

         <dependency>
             <groupId>javax.xml.bind</groupId>
             <artifactId>jaxb-api</artifactId>
             <version>2.3.1</version>
         </dependency>
         <dependency>
             <groupId>com.sun.xml.bind</groupId>
             <artifactId>jaxb-core</artifactId>
             <version>2.3.0.1</version>
         </dependency>
         <dependency>
             <groupId>com.sun.xml.bind</groupId>
             <artifactId>jaxb-impl</artifactId>
             <version>2.4.0-b180830.0438</version>
         </dependency>
         <dependency>
             <groupId>com.sun.activation</groupId>
             <artifactId>javax.activation</artifactId>
             <version>1.2.0</version>
         </dependency>

     </dependencies>

     <build>
         <finalName>spring-security-demo-10-registration</finalName>
         <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
             <plugins>
                 <plugin>
                     <artifactId>maven-clean-plugin</artifactId>
                     <version>3.1.0</version>
                 </plugin>
                 <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                 <plugin>
                     <artifactId>maven-resources-plugin</artifactId>
                     <version>3.0.2</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-compiler-plugin</artifactId>
                     <version>3.8.0</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>2.22.1</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-war-plugin</artifactId>
                     <version>3.2.2</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-install-plugin</artifactId>
                     <version>2.5.2</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-deploy-plugin</artifactId>
                     <version>2.8.2</version>
                 </plugin>
             </plugins>
         </pluginManagement>
     </build>
 </project>
                                    

spring app configuration


 package com.danielbyun.loginRegistrationMultiroles.config;

 import com.mchange.v2.c3p0.ComboPooledDataSource;
 import org.springframework.context.annotation.Bean;
 import org.springframework.core.env.Environment;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
 import org.springframework.web.servlet.ViewResolver;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 import org.springframework.web.servlet.view.InternalResourceViewResolver;

 import javax.sql.DataSource;
 import java.beans.PropertyVetoException;
 import java.util.Properties;
 import java.util.logging.Logger;

 @Configuration
 @EnableWebMvc
 @ComponentScan(basePackages = "com.danielbyun.loginRegistrationMultiroles.*")
 @PropertySource("classpath:persistence-mysql.properties")
 public class AppConfig implements WebMvcConfigurer {
     @Autowired
     private Environment environment;

     // logger for diagnostic stuff
     private Logger logger = Logger.getLogger(getClass().getName());

     // view resolver
     @Bean
     public ViewResolver viewResolver() {
         InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();

         internalResourceViewResolver.setPrefix("/WEB-INF/view/");
         internalResourceViewResolver.setSuffix(".jsp");

         return internalResourceViewResolver;
     }

     // resource handler
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
         resourceHandlerRegistry
                 .addResourceHandler("/resource/**")
                 .addResourceLocations("/resource/");
     }

     // define bean for datasource
     @Bean
     public DataSource dataSource() {
         // create connection pool
         ComboPooledDataSource dataSource = new ComboPooledDataSource();

         // set the jdbc driver class
         try {
             // read db configs from the resource file
             dataSource.setDriverClass(environment.getProperty("jdbc.driverClass"));
         } catch (PropertyVetoException exc) {
             throw new RuntimeException(exc);
         }

         // log the connection props
         // check if we're reading the correct data from the properties file
         logger.info(">> jdbc.url = " + environment.getProperty("jdbc.url"));
         logger.info(">> jdbc.user = " + environment.getProperty("jdbc.user"));

         // set database connection props
         dataSource.setJdbcUrl(environment.getProperty("jdbc.url"));
         dataSource.setUser(environment.getProperty("jdbc.user"));
         dataSource.setPassword(environment.getProperty("jdbc.password"));

         // set connection pool props
         dataSource.setInitialPoolSize(getIntProperties("connection.pool.initialPoolSize"));
         dataSource.setMinPoolSize(getIntProperties("connection.pool.minPoolSize"));
         dataSource.setMaxPoolSize(getIntProperties("connection.pool.maxPoolSize"));
         dataSource.setMaxIdleTime(getIntProperties("connection.pool.maxIdleTime"));

         return dataSource;
     }

     private int getIntProperties(String propName) {
         String propVal = environment.getProperty(propName);

         // now convert string into int
         int intPropVal = Integer.parseInt(propVal);

         return intPropVal;
     }

     // get hibernate properties from .properties file
     private Properties getHibernateProperties() {
         // set hibernate properties
         Properties props = new Properties();

         props.setProperty("hibernate.dialect", environment.getProperty("hibernate.dialect"));
         props.setProperty("hibernate.show_sql", environment.getProperty("hibernate.show_sql"));

         return props;
     }
 }
                                    

security web application initializer


 package com.danielbyun.loginRegistrationMultiroles.config;

 import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

 public class SecurityWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
     @Override
     protected Class[] getRootConfigClasses() {
         return null;
     }

     @Override
     protected Class[] getServletConfigClasses() {
         return new Class[]{AppConfig.class};
     }

     @Override
     protected String[] getServletMappings() {
         return new String[]{"/"};
     }
 }
                                    

spring mvc dispatcher servlet


 package com.danielbyun.loginRegistrationMultiroles.config;

 import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

 public class SpringMvcDispatcherServletInitializer extends AbstractSecurityWebApplicationInitializer {
 }
                                    

spring security configuration


 package com.danielbyun.loginRegistrationMultiroles.config;

 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.provisioning.JdbcUserDetailsManager;
 import org.springframework.security.provisioning.UserDetailsManager;

 import javax.sql.DataSource;

 @Configuration
 @EnableWebSecurity
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
     @Autowired
     private DataSource dataSource;

     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         // use jdbc authentication
         // tells spring security to use jdbc authentication with our data source
         auth.jdbcAuthentication().dataSource(dataSource);
     }

     @Override
     public void configure(WebSecurity web) throws Exception {
         web.ignoring().antMatchers("/resource/css/**", "/resource/js/**");
     }

     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.authorizeRequests().antMatchers("/resource/css/**", "/resource/js/**", "/resource/images/**").permitAll();

         http.authorizeRequests()
                 .antMatchers("/").hasRole("EMPLOYEE")
                 .antMatchers("/managers/**").hasRole("MANAGER")
                 .antMatchers("/admin/**").hasRole("ADMIN")
                 .and()
                 .formLogin()
                 .loginPage("/login")
                 .loginProcessingUrl("/authenticateTheUser")
                 .permitAll()
                 .and()
                 .logout().permitAll()
                 .and()
                 .exceptionHandling().accessDeniedPage("/access-denied");
     }
     @Bean
     public UserDetailsManager userDetailsManager() {
         JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
         jdbcUserDetailsManager.setDataSource(dataSource);

         return jdbcUserDetailsManager;
     }
 }
                                    

mysql properties

 #
 #   JDBC connection properties
 #
 jdbc.driverClass=com.mysql.cj.jdbc.Driver
 jdbc.url=jdbc:mysql://localhost:3306/spring-login-registration-multiroles?useSSL=false&serverTimezone=UTC&useUnicode=true&allowPublicKeyRetrieval=true
 jdbc.user=[]
 jdbc.password=[]
 #
 #   Connection pool properties
 #
 connection.pool.initialPoolSize=5
 connection.pool.minPoolSize=5
 connection.pool.maxPoolSize=20
 connection.pool.maxIdleTime=3000
 #
 #   hibernate properties
 #
 hibernate.dialect=org.hibernate.dialect.MySQLDialect
 hibernate.show_sql=true
                                    

entities


 package com.danielbyun.loginRegistrationMultiroles.user;

 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;

 public class AppUser {
     @NotNull(message = "is required")
     @Size(min = 1, message = "is required")
     private String userName;

     @NotNull(message = "is required")
     @Size(min = 1, message = "is required")
     private String password;

     private String formRole;

     public AppUser() {
     }

     public AppUser(@NotNull(message = "is required") @Size(min = 1, message = "is required") String userName, @NotNull(message = "is required") @Size(min = 1, message = "is required") String password, String formRole) {
         this.userName = userName;
         this.password = password;
         this.formRole = formRole;
     }

     public String getUserName() {
         return userName;
     }

     public void setUserName(String userName) {
         this.userName = userName;
     }

     public String getPassword() {
         return password;
     }

     public void setPassword(String password) {
         this.password = password;
     }

     public String getFormRole() {
         return formRole;
     }

     public void setFormRole(String formRole) {
         this.formRole = formRole;
     }

     @Override
     public String toString() {
         return "AppUser{" +
                 "userName='" + userName + '\'' +
                 ", password='" + password + '\'' +
                 ", formRole='" + formRole + '\'' +
                 '}';
     }
 }
                                    

controllers

main app controller


 package com.danielbyun.loginRegistrationMultiroles.controller;

 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;

 @Controller
 public class MainController {
     @GetMapping("/")
     public String showHome() {
         return "home";
     }

     @GetMapping("/managers")
     public String showLeaders() {
         return "leaders";
     }

     @GetMapping("/admin")
     public String showOwner() {
         return "admin";
     }
 }
                                    

login controller


 package com.danielbyun.loginRegistrationMultiroles.controller;

 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;

 @Controller
 public class LoginController {
     @GetMapping("/login")
     public String showLoginPage() {
         return "login";
     }

     @GetMapping("/access-denied")
     public String accessDeniedPage() {
         return "access-denied";
     }
 }
                                    

registration controller


 package com.danielbyun.loginRegistrationMultiroles.controller;

 import com.danielbyun.loginRegistrationMultiroles.user.AppUser;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.propertyeditors.StringTrimmerEditor;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.provisioning.UserDetailsManager;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.WebDataBinder;
 import org.springframework.web.bind.annotation.*;

 import javax.annotation.PostConstruct;
 import javax.validation.Valid;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;

 @Controller
 @RequestMapping("/register")
 public class RegistrationController {
     @Autowired
     private UserDetailsManager userDetailsManager;

     private Map<String, String> roles;
     private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
     private Logger logger = Logger.getLogger(getClass().getName());

     @InitBinder
     public void initBinder(WebDataBinder dataBinder) {
         StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
         dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
     }

     // load roles
     @PostConstruct
     protected void loadRoles() {
         roles = new LinkedHashMap<String, String>();

         roles.put("ROLE_EMPLOYEE", "Employee");
         roles.put("ROLE_MANAGER", "Manager");
         roles.put("ROLE_ADMIN", "Admin");
     }

     // display registration from
     @GetMapping("/showRegistrationForm")
     public String showRegistrationForm(Model model) {
         model.addAttribute("user", new AppUser());
         model.addAttribute("roles", roles);

         return "registration-form";
     }

     @PostMapping("/processRegistrationForm")
     public String processRegistrationForm(
             @Valid @ModelAttribute("user") AppUser user,
             BindingResult bindingResult,
             Model model) {
         String userName = user.getUserName();
         String role = user.getFormRole();

         logger.info("processing registration form for: " + userName);
         logger.info("role: " + role);

         if (bindingResult.hasErrors()) {
             model.addAttribute("user", new AppUser());
             model.addAttribute("roles", roles);
             model.addAttribute("registrationError", "user name // password cannot be empty.");

             logger.warning("user name // password cannot be empty.");

             return "registration-form";
         }
         boolean userExists = doesUserExist(userName);

         if (userExists) {
             model.addAttribute("user", new AppUser());
             model.addAttribute("roles", roles);
             model.addAttribute("registrationError", "user name already exists");

             logger.warning("user name exists already");

             return "registration-form";
         }

         // encode password usuing bCrypt
         String encodedPassword = passwordEncoder.encode(user.getPassword());
         // add {bcrypt} prefix to identify the bcrypt encryption
         encodedPassword = "{bcrypt}" + encodedPassword;

         // create authorityList
         List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList();
         // default role = ROLE_EMPLOYEE
         authorities.add(new SimpleGrantedAuthority("ROLE_EMPLOYEE"));

         String formRole = user.getFormRole();
         logger.info(formRole);

         // add ROLE_MANAGER when ROLE_ADMIN is selected
         if (formRole.equals("ROLE_ADMIN")) {
             authorities.add(new SimpleGrantedAuthority("ROLE_MANAGER"));
         }

         if (!formRole.equals("ROLE_EMPLOYEE")) {
             authorities.add(new SimpleGrantedAuthority(formRole));
         }

         org.springframework.security.core.userdetails.User tempUser = new org.springframework.security.core. userdetails.User(userName, encodedPassword, authorities);

         userDetailsManager.createUser(tempUser);

         logger.info("successfully created user: " + userName);

         return "registration-confirmation";
     }

     private boolean doesUserExist(String userName) {
         boolean exists = userDetailsManager.userExists(userName);

         logger.info("checking if user exists: " + userName);
         logger.info("user: " + userName + ", exists: " + exists);

         return exists;
     }
 }
                                    

project database schema

database schema

check out part 2 for the view layer

github link to project

register // login app that supports multi-role feature (part 2)

view layer

the login page

since the security config file calls that all pages be accessible if they have at least an 'EMPLOYEE' role, the main page will be the login page.


 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
 <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <html>
     <head>
         <title>spring-security-demo</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <!-- materialized css -->
         <!-- Compiled and minified CSS -->
         <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
         <!-- Compiled and minified JavaScript -->
         <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
         <link rel="stylesheet" href="${pageContext.request.contextPath}/resource/css/reset.css">
         <link rel="stylesheet" href="${pageContext.request.contextPath}/resource/css/home.css">
     </head>
     <body>
         <header class="container row col s12 homeHeader">
             <h1 class="col s12">welcome, <security:authentication property="principal.username"/></h1>
             <!-- add a logout button -->
             <form:form action="${pageContext.request.contextPath}/logout" method="post" class="col">
                 <button class="waves-effect waves-light btn-small" type="submit" value="logout">logout</button>
             </form:form>
         </header>
         <main class="container row">
             <div>
                 <p class="col s12 row">
                     <security:authentication property="principal.username"/>, you have the following role(s):
                     <security:authentication property="principal.authorities"/>
                 </p>
             </div>
             <!-- add a link to point to /leaders.. this is for the owner -->
             <security:authorize access="hasRole('ADMIN')">
                 <p class="col s12">
                     <a href="${pageContext.request.contextPath}/admin">admin dashboard</a>
                     (only for the admin)
                 </p>
             </security:authorize>
             <!-- add a link to point to /leaders.. this is for the managers -->
             <security:authorize access="hasRole('MANAGER')">
                 <p class="col s12">
                     <a href="${pageContext.request.contextPath}/managers">manager dashboard</a>
                     (only for the managers)
                 </p>
             </security:authorize>

         </main>
     </body>
 </html>
                                    

login page

login page

login failed

log in failed

login successful

employee view

employee loggedin

manager view

manager loggedin

admin view

admin logged in

successfully logged out

log out

registration page

registration page

registration with roles options out

registration error

registration process

registration successful

database update

user

roles

log in with the new user

log in with new user

log in with new user successful

access-denied example (manually trying to access a page that's not granted to employees)

employee logged in

access-denied

final thoughts


as you can see, a lot can be accomplished with less lines of code when spring mvc, spring security is implemented.

spring security provides a lot of security features that can be implemented with just few lines of code

unlike the first project, the password was encrypted, we were able to give authorities to users with specific users, and display certain elements depending on authorities with just one configuration class.

things i would like to improve on:

i would like to be able to save more user details on registration, not just the username // password.

check the next project, 'gym crm app', where i try to implement that while mocking a crm app that can be used for a local gym or any small business

gym CRM app

spring logo

This web app was created using Spring Boot, Spring Security, MySQL, JPA (Hibernate), and Thymeleaf was used for the view layer.

this app tracks customers, employees, and has a link to the gym's e-commerce site.

demo

    roles:
  • - employees: can view customers
  • - managers: can view // add // edit // delete customers, can view employees
  • - admin: can view // add // edit // delete customers and employees

web page flow:

try logging in with these credentials!

  • for admin:
  • username: admin, password: test123
  • for manager:
  • username: manager, password: test123
  • for employee:
  • username: employee, password: test123

index page (not logged in)

index page

index page for an employee
* as you can see, only the customer list is visible (can edit / delete customers)

index page for an employee

index page for a manager
* as you can see, only the customer list is visible also, (can edit / delete customers, only see the list of employees)

index page for a manager

index page for an admin
* as you can see, both the customer list and employee list are visible. (can edit / delete customers and employees)

index page for an admin

sign in page

login page

home page

home page

customer list

customer list

add new customer

new new customer new new customer

customer added

customer added

update customer

update customer

customer updated

customer updated

employee list

employee list

add new employee

add new employee add new employee

employee added

employee added

update employee

update employee

employee updated

employee updated

lastly, one of the most important features in Spring Security: when a user who's not authorized tries to manually enter the url to attempt to enter

access-denied

final thoughts


1) I'd like to add more information to the customers / users, such as an image of both customer and users.

2) I'd like to convert this project into incorporating Rest API to store customers / employees data and retrieving through the API.

3) I'd also like to have an e-commerce site that goes along with this application.

check out my next project, where I write up a general e-commerce site

a flowershop e-commerce site (live demo site coming soon)

This web app was created using Spring Boot, Spring Security, MySQL, JPA (Hibernate), and Thymeleaf was used for the view layer.

This app features a flowershop page (main site for the customers) and an admin panel that only admins can access.

since this project is too big to display using bootstrap modals, I have deployed the site live so that you can try using it.

*only the configuration images will be uploaded.

*for demo purposes only, please do not input any confidential information (such as credit card information)

ERD graph

ERD graph for flowershop app

project dependencies (pom.xml)

configurations

demo link

a flowershop e-commerce site (live demo site coming soon)

This web app was created using Spring Boot, Spring Security, MySQL, JPA (Hibernate), and React JS.

This app features a personal project management application that a user can log into to perform CRUD operations on their projects and the tasks that are associated with them.

since this project is too big to display using bootstrap modals, I have deployed the site live so that you can try using it.

*only the configuration images will be uploaded.

project dependencies (pom.xml)

configurations

demo link

  • about me
  • my projects
  • contact me