Model 1 Board

Model 1 is a web application that extracts Java code from JSPs, turns them into Java classes, and uses them.

The bulletin board code in the previous section contains the JDBC code in the JSP. Unfortunately, such code is a nightmare in terms of maintenance.

Java programmers should always think about reuse. For reusability, we have to go through a process called Java design. Java design for pure Java applications and Java design for web applications are no different.

Let's start Java design for the bulletin board.

Article.java
package net.java_school.board;

import java.util.Date;

public class Article {
  public static final String LINE_SEPARATOR = System.getProperty("line.separator");

  private int no;
  private String title;
  private String content;
  private Date wdate;
  private int parent;
  private int indent;

  public int getNo() {
    return no;
  }

  public void setNo(int no) {
    this.no = no;
  }

  public String getTitle() {
    return title;
  }

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

  public String getContent() {
    if (content == null) content = "";
    return content;
  }

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

  public String getHtmlContent() {
    if (content == null) {
      return "";
    } else {
      return content.replaceAll(LINE_SEPARATOR, "<br />");
    }
  }

  public Date getWdate() {
    return wdate;
  }

  public void setWdate(Date wdate) {
    this.wdate = wdate;
  }

  public int getParent() {
    return parent;
  }

  public void setParent(int parent) {
    this.parent = parent;
  }

  public int getIndent() {
    return indent;
  }

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

}

Fields of the Article class are mapped to columns of the board table. Thus, there is no indent column in the board table but the indent field in the article class. This field is used to indent the titles in the list.

Fields of the Article class are mapped to columns of the board table except for the indent field used to indent the titles in the list.

The getHtmlContent() is a method that converts the newline character stored in the content column to <br />, which is used in the detailed view page.

public static final String LINE_SEPARATOR = System.getProperty("line.separator");

The LINE_SEPARATOR constant is used in the getHtmlContent() method. The following is the class to manage constants used by the bulletin board program.

WebContants.java
package net.java_school.commons;

public class WebContants {
  //Line Separator
  public final static String LINE_SEPARATOR = System.getProperty("line.separator");
}

Remove the LINE_SEPARATOR declaration from the Article class and modify the Article's getHtmlContent() method as follows:

//.. Omit ..

import net.java_school.commons.WebContants;

public class Article {
  //public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //Remove it
	
  //.. Omit ..

  public String getHtmlContent() {
    if (content == null) {
      return "";
    } else {
      return content.replaceAll(WebContants.LINE_SEPARATOR, "<br />");
    }
  }

  //.. Omit ..
}

You may change the WebContants class to an enum like this:

WebContants.java
package net.java_school.commons;

public enum WebContants {
  lineSeparator (System.getProperty("line.separator"));
	
  public String value;
	
  WebContants (String value) {
    this.value = value;
  }
}

If you change it to enum, the Article's getHtmlContent() method changes as follows:

public String getHtmlContent() {
  if (content == null) {
    return "";
  } else {
    return content.replaceAll(WebContants.lineSeparator.value, "<br />");
  }
}

Next, create a BoardDao class responsible for JDBC. At the end of the name, Dao stands for Data Access Object, which only performs tasks related to data storage. This pattern is called the DAO pattern.

The following section describes the open-source connection pool and how to modify BoardDao to use an open-source connection pool. For this, BoardDao in this section will not use the connection pool itself.

BoardDao.java
package net.java_school.board;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.ArrayList;

import net.java_school.util.Log;

public class BoardDao {
  private static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
  private static final String USER = "scott";
  private static final String PASSWORD = "tiger";
	
  public BoardDao() {
    try {
      Class.forName("oracle.jdbc.driver.OracleDriver");
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
	
  private Connection getConnection() throws SQLException {
    return DriverManager.getConnection(URL, USER, PASSWORD);
  }
	
  private void close(ResultSet rs, PreparedStatement stmt, Connection con) {
    if (rs != null) {
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if (stmt != null) {
      try {
        stmt.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if (con != null) {
      try {
        con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
	
  public List<Article> getBoardList(int start, int end, String keyword) {
    Log log = new Log();
    List<Article> list = new ArrayList<Article>();
    String sql = null;
		
    if (keyword == null || keyword.equals("")) {
      sql = "SELECT no, indent, parent, title, wdate FROM " +
        "(SELECT ROWNUM R, A.* " +
        "FROM " +
        "(SELECT no, level as indent, parent, title, wdate FROM board " +
        "START WITH parent = 0 " +
        "CONNECT BY PRIOR no = parent " +
        "ORDER SIBLINGS BY no DESC) A) " +
        "WHERE R BETWEEN ? AND ?";
    } else {
      keyword = "%" + keyword + "%";
      sql = "SELECT no, indent, parent, title, wdate FROM " +
        "(SELECT ROWNUM R, A.* " +
        "FROM " +
        "(SELECT no, level as indent, parent, title, wdate FROM board " +
        "WHERE title LIKE ? OR content LIKE ? " +
        "START WITH parent = 0 " +
        "CONNECT BY PRIOR no = parent " +
        "ORDER SIBLINGS BY no DESC) A) " +
        "WHERE R BETWEEN ? AND ?";
    }

    Connection con = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
		
    try {
      con = getConnection();
      stmt = con.prepareStatement(sql);
      if (keyword == null || keyword.equals("")) {
        stmt.setInt(1, start);
        stmt.setInt(2, end);
      } else {
        stmt.setString(1, keyword);
        stmt.setString(2, keyword);
        stmt.setInt(3, start);
        stmt.setInt(4, end);
      }
      rs = stmt.executeQuery();
			
      while (rs.next()) {
        Article article = new Article();
        article.setNo(rs.getInt("no"));
        article.setTitle(rs.getString("title"));
        article.setWdate(rs.getDate("wdate"));
        article.setIndent(rs.getInt("indent"));
        list.add(article);
      }
    } catch (SQLException e) {
      log.debug("Error Source : BoardDao.getBoardList() : SQLException");
      log.debug("SQLState : " + e.getSQLState());
      log.debug("Message : " + e.getMessage());
      log.debug("Oracle Error Code : " + e.getErrorCode());
      log.debug("sql : " + sql);
    } finally {
      close(rs, stmt, con);
      log.close();
    }
		
    return list;
  }
	
  public int getTotalRecord(String keyword) {
    Log log = new Log();
    int totalRecord = 0;
    String sql = null;
		
    if (keyword == null || keyword.equals("")) {
      sql = "SELECT count(*) FROM board";
    } else {
      sql = "SELECT count(*) FROM board " +
      "WHERE title LIKE ? OR content LIKE ?";
    }
				
    Connection con = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
		
    try {
      con = getConnection();
      stmt = con.prepareStatement(sql);
      if (keyword != null && !keyword.equals("")) {
        keyword = "%" + keyword + "%";
        stmt.setString(1, keyword);
        stmt.setString(2, keyword);
      }
      rs = stmt.executeQuery();
      rs.next();
      totalRecord = rs.getInt(1);
    } catch (SQLException e) {
      log.debug("Error Source : BoardDao.getTotalRecord() : SQLException");
      log.debug("SQLState : " + e.getSQLState());
      log.debug("Message : " + e.getMessage());
      log.debug("Oracle Error Code : " + e.getErrorCode());
      log.debug("sql : " + sql);
    } finally {
      close(rs, stmt, con);
      log.close();
    }
		
    return totalRecord;
  }

}

The following method produces the numbers needed to paginate the list.

public Map<String, Integer> getNumbersForPaging(int totalRecord, int curPage, int numPerPage, int pagePerBlock) {
  Map<String, Integer> map = new HashMap<String, Integer>();
	
  int totalPage = totalRecord / numPerPage;
  if (totalRecord % numPerPage != 0) totalPage++;
  int totalBlock = totalPage / pagePerBlock;
  if (totalPage % pagePerBlock != 0) totalBlock++;
	
  int block = curPage / pagePerBlock;
  if (curPage % pagePerBlock != 0) block++;
	
  int firstPage = (block - 1) * pagePerBlock + 1;
  int lastPage = block * pagePerBlock;
	
  int prevPage = 0;
  if (block > 1) {
    prevPage = firstPage - 1;
  }

  int nextPage = 0;
  if (block < totalBlock) {
    nextPage = lastPage + 1;
  }
  if (block >= totalBlock) {
    lastPage = totalPage;
  }

  int listItemNo = totalRecord - (curPage - 1) * numPerPage;
  int startRecord = (curPage - 1) * numPerPage + 1;
  int endRecord = curPage * numPerPage;

  map.put("totalPage", totalPage);
  map.put("firstPage", firstPage);
  map.put("lastPage", lastPage);
  map.put("prevPage", prevPage);
  map.put("nextPage", nextPage);
  map.put("listItemNo", listItemNo);
  map.put("startRecord", startRecord);
  map.put("endRecord", endRecord);

  return map;
}    

Modify the list page to use the above method.

/board/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%@ page import="java.util.*" %>
<%!
public Map<String, Integer> getNumbersForPaging(int totalRecord, int curPage, int numPerPage, int pagePerBlock) {

  Map<String, Integer> map = new HashMap<String, Integer>();
	
  int totalPage = totalRecord / numPerPage;
  if (totalRecord % numPerPage != 0) totalPage++;
  int totalBlock = totalPage / pagePerBlock;
  if (totalPage % pagePerBlock != 0) totalBlock++;
	
  int block = curPage / pagePerBlock;
  if (curPage % pagePerBlock != 0) block++;
	
  int firstPage = (block - 1) * pagePerBlock + 1;
  int lastPage = block * pagePerBlock;
	
  int prevPage = 0;
  if (block > 1) {
    prevPage = firstPage - 1;
  }

  int nextPage = 0;
  if (block < totalBlock) {
    nextPage = lastPage + 1;
  }
  if (block >= totalBlock) {
    lastPage = totalPage;
  }

  int listItemNo = totalRecord - (curPage - 1) * numPerPage;
  int startRecord = (curPage - 1) * numPerPage + 1;
  int endRecord = curPage * numPerPage;

  map.put("totalPage", totalPage);
  map.put("firstPage", firstPage);
  map.put("lastPage", lastPage);
  map.put("prevPage", prevPage);
  map.put("nextPage", nextPage);
  map.put("listItemNo", listItemNo);
  map.put("startRecord", startRecord);
  map.put("endRecord", endRecord);

  return map;
}
%>
<%
request.setCharacterEncoding("UTF-8");
int curPage = request.getParameter("curPage") == null ? 1 : Integer.parseInt(request.getParameter("curPage"));
String keyword = request.getParameter("keyword");
if (keyword == null) keyword = "";
BoardDao dao = new BoardDao();
int totalRecord = dao.getTotalRecord(keyword);
Map<String, Integer> numbers = this.getNumbersForPaging(totalRecord, curPage, 10, 5);
int startRecord = numbers.get("startRecord");
int endRecord = numbers.get("endRecord");
List<Article> list = dao.getBoardList(startRecord, endRecord, keyword);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>List</title>
</head>
<body style="font-size: 11px;">
<h1>List</h1>
<%
int listItemNo = numbers.get("listItemNo");//This value is the number assigned to the list item on the list page.It is not a value stored in the no column.
for (int i = 0; i < list.size(); i++) {
  Article article = list.get(i);
  int indent = article.getIndent();
  for (int j = 0; j < indent; j++) {
    out.println("&nbsp;&nbsp;");
  }
  if(indent != 1) {
    out.println("↳");
  }
%>
<%=listItemNo %>
<a href="view.jsp?no=<%=article.getNo() %>&curPage=<%=curPage %>&keyword=<%=keyword %>"><%=article.getTitle() %></a>
<%=article.getWdate() %><br />
<hr />
<%
listItemNo--;
}
int prevPage = numbers.get("prevPage");
if (prevPage != 0) {
%>
  <a href="list.jsp?curPage=<%=prevPage %>&keyword=<%=keyword %>">[<]</a>
<%
}
int firstPage = numbers.get("firstPage");
int lastPage = numbers.get("lastPage");
for (int i = firstPage; i <= lastPage; i++) {
%>
  <a href="list.jsp?curPage=<%=i %>&keyword=<%=keyword %>">[<%=i %>]</a>
<%
}
int nextPage = numbers.get("nextPage");
if (nextPage != 0) {
%>
  <a href="list.jsp?curPage=<%=nextPage %>&keyword=<%=keyword %>">[>]</a>
<%
}
%>				
<p>
<a href="write_form.jsp?curPage=<%=curPage %>&keyword=<%=keyword %>">New</a>
</p>
<form method="get">
  <input type="text" size="10" maxlength="30" name="keyword" />
  <input type="submit" value="Search" />
</form>
</body>
</html>

Article and BoardDao are all you need to create a simple bulletin board. But, most web programs have a class that acts as only one reception desk to web components like JSPs and servlets. These class's names end up with Service. This pattern is called the Service pattern.

Create a BoardService class as shown below.

BoardService.java
package net.java_school.board;

import java.util.Map;
import java.util.HashMap;
import java.util.List;

public class BoardService {

  private BoardDao dao = new BoardDao();
	
  public BoardService() {}
	
  public Map<String, Integer> getNumbersForPaging(int totalRecord, int curPage, int numPerPage, int pagePerBlock) {
    Map<String, Integer> map = new HashMap<String, Integer>();
		
    int totalPage = totalRecord / numPerPage;
    if (totalRecord % numPerPage != 0) totalPage++;
    int totalBlock = totalPage / pagePerBlock;
    if (totalPage % pagePerBlock != 0) totalBlock++;
		
    int block = curPage / pagePerBlock;
    if (curPage % pagePerBlock != 0) block++;
		
    int firstPage = (block - 1) * pagePerBlock + 1;
    int lastPage = block * pagePerBlock;
		
    int prevPage = 0;
    if (block > 1) {
      prevPage = firstPage - 1;
    }
	
    int nextPage = 0;
    if (block < totalBlock) {
      nextPage = lastPage + 1;
    }
    if (block >= totalBlock) {
      lastPage = totalPage;
    }
	
    int listItemNo = totalRecord - (curPage - 1) * numPerPage;
    int startRecord = (curPage - 1) * numPerPage + 1;
    int endRecord = curPage * numPerPage;
	
    map.put("totalPage", totalPage);
    map.put("firstPage", firstPage);
    map.put("lastPage", lastPage);
    map.put("prevPage", prevPage);
    map.put("nextPage", nextPage);
    map.put("listItemNo", listItemNo);
    map.put("startRecord", startRecord);
    map.put("endRecord", endRecord);
	
    return map;
  }
	
  public List<Article> getBoardList(int startRecord, int endRecord, String keyword) {
    return dao.getBoardList(startRecord, endRecord, keyword);
  }
    
  public int getTotalRecord(String keyword) {
    return dao.getTotalRecord(keyword);
  }
}

Now that JSPs only use the BoardService. Notice that the getNumbersForPaging() method declared in the list page has been moved to BoardService.

Modify the list page to use only BoardService.

list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%@ page import="net.java_school.commons.*" %>
<%@ page import="java.util.*" %>
<%
request.setCharacterEncoding("UTF-8");
int curPage = (request.getParameter("curPage") == null ? 1 : Integer.parseInt(request.getParameter("curPage")));
String keyword = request.getParameter("keyword");
if (keyword == null) keyword = "";
BoardService service = new BoardService();
int totalRecord = service.getTotalRecord(keyword);
Map<String, Integer> numbers = service.getNumbersForPaging(totalRecord, curPage, 10, 5);
int startRecord = numbers.get("startRecord");
int endRecord = numbers.get("endRecord");
List<Article> list = service.getBoardList(startRecord, endRecord, keyword);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>List</title>
</head>
<body style="font-size: 11px;">
<h1>List</h1>
<%
int listItemNo = numbers.get("listItemNo");
for (int i = 0; i < list.size(); i++) {
  Article article = list.get(i);
  int indent = article.getIndent();
  for (int j = 0; j < indent; j++) {
    out.println("&nbsp;&nbsp;");
  }
  if(indent != 1) {
    out.println("↳");
  }
%>
<%=listItemNo %>
<a href="view.jsp?no=<%=article.getNo() %>&curPage=<%=curPage %>&keyword=<%=keyword %>"><%=article.getTitle() %></a>
<%=article.getWdate() %><br />
<hr />
<%
listItemNo--;
}
int prevPage = numbers.get("prevPage");
if (prevPage != 0) {
%>
  <a href="list.jsp?curPage=<%=prevPage %>&keyword=<%=keyword %>">[<]</a>
<%
}
int firstPage = numbers.get("firstPage");
int lastPage = numbers.get("lastPage");
for (int i = firstPage; i <= lastPage; i++) {
%>
  <a href="list.jsp?curPage=<%=i %>&keyword=<%=keyword %>">[<%=i %>]</a>
<%
}
int nextPage = numbers.get("nextPage");
if (nextPage != 0) {
%>
  <a href="list.jsp?curPage=<%=nextPage %>&keyword=<%=keyword %>">[>]</a>
<%
}
%>				
<p>
<a href="write_form.jsp?curPage=<%=curPage %>&keyword=<%=keyword %>">New</a>
</p>
<form method="get">
  <input type="text" size="10" maxlength="30" name="keyword" />
  <input type="submit" value="Search" />
</form>	
</body>
</html>

I'm not going to use BoardWriter anymore because BoardService must provide all the board's logic. I'll use write.jsp instead of BoardWriter.

Modify the action attribute value of the form tag in write_form.jsp file to write.jsp.

write_form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("UTF-8");
String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>New</title>
</head>
<body>
<h1>New</h1>
<form action="write.jsp" method="post">
<table>
<tr>
  <td>Title</td>
  <td><input type="text" name="title" size="50"></td>
</tr>
<tr>
  <td colspan="2">
    <textarea name="content" rows="20" cols="100"></textarea>
  </td>
</tr>
<tr>
  <td colspan="2">
    <input type="submit" value="Submit">
    <input type="reset" value="Reset">
    <a href="list.jsp?curPage=<%=curPage %>&keyword=<%=keyword %>">List</a>
  </td>
</tr>
</table>
</form>
</body>
</html>

Add the following method to BoardDao. BoardDao's insert() method has the same logic as BoardWriter.

BoardDao.java
public void insert(Article article) {
  Log log = new Log();
  String sql = "INSERT INTO board (no, title, content, wdate, parent) "
    + "VALUES (board_no_seq.nextval, ?, ?, sysdate, 0)";
		
  Connection con = null;
  PreparedStatement stmt = null;
	
  try {
    con = getConnection();
    stmt = con.prepareStatement(sql);
    stmt.setString(1, article.getTitle());
    stmt.setString(2, article.getContent());
    stmt.executeUpdate();
  } catch (SQLException e) {
    log.debug("Error Source : BoardDao.insert() : SQLException");
    log.debug("SQLState : " + e.getSQLState());
    log.debug("Message : " + e.getMessage());
    log.debug("Oracle Error Code : " + e.getErrorCode());
    log.debug("sql : " + sql);
  } finally {
    close(null, stmt, con);
    log.close();
  }
}

Add the following method to BoardService.

BoardService.java
public void write(Article article) {
  dao.insert(article);
}

Create write.jsp as shown below.

write.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%
request.setCharacterEncoding("UTF-8");
String title = request.getParameter("title");
String content = request.getParameter("content");

Article article = new Article();
article.setTitle(title);
article.setContent(content);

BoardService service= new BoardService();
service.write(article);

response.sendRedirect("list.jsp");
%>

Add the selectOne() method to BoardDao.

BoardDao.java
public Article selectOne(int no) {
  Log log = new Log();
	
  Article article = null;
  String sql = "SELECT no, title, content, wdate FROM board WHERE no = ?";
	
  Connection con = null;
  PreparedStatement stmt = null;
  ResultSet rs = null;
	
  try {
    con = getConnection();
    stmt = con.prepareStatement(sql);
    stmt.setInt(1, no);
    rs = stmt.executeQuery();
    while (rs.next()) {
      article = new Article();
      article.setNo(rs.getInt("no"));
      article.setTitle(rs.getString("title"));
      article.setContent(rs.getString("content"));
      article.setWdate(rs.getDate("wdate"));
    }
  } catch (SQLException e) {
    log.debug("Error Source : BoardDao.selectOne() : SQLException");
    log.debug("SQLState : " + e.getSQLState());
    log.debug("Message : " + e.getMessage());
    log.debug("Oracle Error Code : " + e.getErrorCode());
    log.debug("sql : " + sql);
  } finally {
    close(rs, stmt, con);
    log.close();
  }

  return article;
}

Add the following method to BoardService. This method is used on the detailed view page.

BoardService.java
public Article getArticle(int no) {
  return dao.selectOne(no);
}

Modify the detailed view page as shown below.

view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%
request.setCharacterEncoding("UTF-8");
int no = Integer.parseInt(request.getParameter("no"));
String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");
if (keyword == null) keyword = "";
BoardService service = new BoardService();
Article article = service.getArticle(no);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>View</title>
<script type="text/javascript">
function goModify(no,curPage,keyword) {
  location.href="modify_form.jsp?no=" + no + "&curPage=" + curPage + "&keyword=" + keyword;
}

function goDelete(no,curPage,keyword) {
  var check = confirm("Are you sure you want to delete it?");
  if (check) {
    location.href="del.jsp?no=" + no + "&curPage=" + curPage + "&keyword=" + keyword;
  }
}
</script>
</head>
<body>
<h1>View</h1>
<h2>Title: <%=article.getTitle() %>, Date created: <%=article.getWdate() %></h2>
<p>
<%=article.getHtmlContent() %>
</p>
<a href="list.jsp?curPage=<%=curPage %>&keyword=<%=keyword %>">List</a>
<input type="button" value="Modify" onclick="javascript:goModify('<%=no %>','<%=curPage %>','<%=keyword %>')">
<input type="button" value="Delete" onclick="javascript:goDelete('<%=no %>','<%=curPage %>','<%=keyword %>')">
<a href="reply_form.jsp?no=<%=no %>&curPage=<%=curPage %>&keyword=<%=keyword %>">Reply</a>
</body>
</html>

Add the following update() method to BoardDao.

BoardDao.java
public void update(Article article) {
  Log log = new Log();
  String sql = "UPDATE board SET title = ?, content = ? WHERE no = ?";		
  Connection con = null;
  PreparedStatement stmt = null;
  try {
    con = getConnection();
    stmt = con.prepareStatement(sql);
    stmt.setString(1, article.getTitle());
    stmt.setString(2, article.getContent());
    stmt.setInt(3, article.getNo());
    stmt.executeUpdate();
  } catch (SQLException e) {
    log.debug("Error Source : BoardDao.update() : SQLException");
    log.debug("SQLState : " + e.getSQLState());
    log.debug("Message : " + e.getMessage());
    log.debug("Oracle Error Code : " + e.getErrorCode());
    log.debug("sql : " + sql);
  } finally {
    close(null, stmt, con);
    log.close();
  }
}

Add the following method to BoardService. This method is used on the modify.jsp page. The modify.jsp does the same thing as BoardModifier. BoardModifier is deprecated.

BoardService.java
public void modify(Article article) {
  dao.update(article);
}

Modify the modify_form.jsp to use only BoardService.

modify_form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.sql.*" %>
<%@ page import="net.java_school.board.*" %>
<%
request.setCharacterEncoding("UTF-8");

int no = Integer.parseInt(request.getParameter("no"));
String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");

BoardService service = new BoardService();
Article article = service.getArticle(no);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Edit posts</title>
</head>
<body>
<h1>Edit posts</h1>
<form action="modify.jsp" method="post">
<input type="hidden" name="no" value="<%=no %>">
<input type="hidden" name="curPage" value="<%=curPage %>">
<input type="hidden" name="keyword" value="<%=keyword %>">
<table>
<tr>
  <td>Title</td>
  <td><input type="text" name="title" size="50" value="<%=article.getTitle() %>" /></td>
</tr>
<tr>
  <td colspan="2">
    <textarea name="content" rows="30" cols="100"><%=article.getContent() %></textarea>
  </td>
</tr>
<tr>
  <td colspan="2">
    <input type="submit" value="Submit">
    <input type="reset" value="Reset">
    <a href="view.jsp?no=<%=no %>&curPage=<%=curPage %>&keyword=<%=keyword %>">View</a>
  </td>
</tr>
</table>
</form>
</body>
</html>

Create a modify.jsp as shown below.

modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%
request.setCharacterEncoding("UTF-8");
int no = Integer.parseInt(request.getParameter("no"));
String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");
String title = request.getParameter("title");
String content = request.getParameter("content");

Article article = new Article();
article.setNo(no);
article.setTitle(title);
article.setContent(content);

BoardService service= new BoardService();
service.modify(article);
keyword = java.net.URLEncoder.encode(keyword,"UTF-8");
response.sendRedirect("view.jsp?no=" + no + "&curPage=" + curPage + "&keyword=" +  keyword);
%>

Add a delete() method to BoardDao. This method is used on the del.jsp page. The del.jsp does the same thing as BoardDeleter. BoardDeleter is deprecated.

BoardDao.java
public void delete(int no) {
  Log log = new Log();
	
  String sql1 = "SELECT count(*) FROM board WHERE parent = ?";
  String sql2 = "DELETE FROM board WHERE no = ?";
	
  Connection con = null;
  PreparedStatement stmt1 = null;
  PreparedStatement stmt2 = null;
  ResultSet rs = null;
	
  boolean check = false; //If true, delete post
	
  try {
    con = getConnection();
    stmt1 = con.prepareStatement(sql1);
    stmt1.setInt(1, no);
    rs = stmt1.executeQuery();
    rs.next();
    int num = rs.getInt(1);
    if (num == 0) {
      check = true;
    }
    if (check == true) {
      stmt2 = con.prepareStatement(sql2);
      stmt2.setInt(1, no);
      stmt2.executeUpdate();
    }
  } catch (SQLException e) {
    log.debug("Error Source : BoardDao.delete() : SQLException");
    log.debug("SQLState : " + e.getSQLState());
    log.debug("Message : " + e.getMessage());
    log.debug("Oracle Error Code : " + e.getErrorCode());
    log.debug("sql : " + sql1);
  } finally {
    if (rs != null) {
      try {
        rs.close();
      } catch (SQLException e) {}
    }
    if (stmt1 != null) {
      try {
        stmt1.close();
      } catch (SQLException e) {}
    }
    if (stmt2 != null) {
      try {
        stmt2.close();
      } catch (SQLException e) {}
    }
    if (con != null) {
      try {
        con.close();
      } catch (SQLException e) {}
    }
    log.close();
  }
}

Add the following method to BoardService.

BoardService.java
public void delete(int no) {
  dao.delete(no);
}

Create a del.jsp as follows.

del.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%
request.setCharacterEncoding("UTF-8");
int no = Integer.parseInt(request.getParameter("no"));
String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");

BoardService service= new BoardService();
service.delete(no);
keyword = java.net.URLEncoder.encode(keyword,"UTF-8");
response.sendRedirect("list.jsp?curPage=" + curPage + "&keyword=" +  keyword);
%>

Add the following reply() method to BoardDao.

BoardDao.java
public void reply(Article article) {
  Log log = new Log();
	
  String sql = "INSERT INTO board " + 
      "(no, parent, title, content, wdate) " + 
      "VALUES (board_no_seq.nextval, ?, ?, ?, sysdate)";
			
  Connection con = null;
  PreparedStatement stmt = null;
	
  try {
    con = getConnection();
    stmt = con.prepareStatement(sql);
    stmt.setInt(1, article.getParent());
    stmt.setString(2, article.getTitle());
    stmt.setString(3, article.getContent());
    stmt.executeUpdate();
  } catch (SQLException e) {
    log.debug("Error Source:BoardDao.reply() : SQLException");
    log.debug("SQLState : " + e.getSQLState());
    log.debug("Message : " + e.getMessage());
    log.debug("Oracle Error Code : " + e.getErrorCode());
    log.debug("sql : " + sql);
  } finally {
    if (stmt != null) {
      try {
        stmt.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if (con != null) {
      try {
        con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    log.close();
  }
}

Add the following reply() method to BoardService. This method is used in the reply.jsp page. The reply.jsp does the same thing as BoardReplier. BoardReplier is deprecated.

BoardService.java
public void reply(Article article) {
  dao.reply(article);
}
reply_form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%@ page import="net.java_school.commons.*" %>
<%
request.setCharacterEncoding("UTF-8");

int no = Integer.parseInt(request.getParameter("no"));
String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");

BoardService service = new BoardService();
Article article = service.getArticle(no);
String content = article.getContent();

//Add > for each line of the parent content.
content = content.replaceAll(WebContants.lineSeparator.value, WebContants.lineSeparator.value + ">");
content = WebContants.lineSeparator.value + WebContants.lineSeparator.value + ">" + content;
%>
<html>
<head>
<meta charset="UTF-8" />
<title>Reply</title>
</head>
<body>

<h1>Reply</h1>

<form action="reply.jsp" method="post">
<input type="hidden" name="no" value="<%=no %>" />
<input type="hidden" name="curPage" value="<%=curPage %>" />
<input type="hidden" name="keyword" value="<%=keyword %>" />
Title : <input type="text" name="title" size="45" value="<%=article.getTitle() %>" /><br />
<textarea name="content" rows="10" cols="60"><%=content %></textarea><br />
<input type="submit" value="Submit" />
<input type="reset" value="Reset" /><br />
</form>
<a href="view.jsp?no=<%=no %>&curPage=<%=curPage %>&keyword=<%=keyword %>">View</a>
</body>
</html>

Create a reply.jsp as shown below.

reply.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.board.*" %>
<%
request.setCharacterEncoding("UTF-8");

int parent = Integer.parseInt(request.getParameter("no"));
String title = request.getParameter("title");
String content = request.getParameter("content");

String curPage = request.getParameter("curPage");
String keyword = request.getParameter("keyword");

Article article = new Article();
article.setParent(parent);
article.setTitle(title);
article.setContent(content);

BoardService service= new BoardService();
service.reply(article);

keyword = java.net.URLEncoder.encode(keyword,"UTF-8");
response.sendRedirect("list.jsp?curPage=" + curPage + "&keyword=" + keyword);
%>