Java Web Programming with Eclipse contents
Last modified February 15, 2011 05:10 am

back next

Wiki Application (continued)

Video

Persistence Classes

Overview

In this section, you will create the classes that store and manage the persistent data of the application. Specifically, a page class and its corresponding DAO will be defined. In preparation for adding additional persistent classes, we create a parent DAO that the page DAO extends. Data access object classes for other persistent classes can then be added more easily by subclassing the parent DAO.

There is one important difference between the persistence system in the wiki application and the publisher application: the wiki application will use natural primary keys as opposed to surrogate primary keys used in the publsiher application. This is done for two reasons. First, by using the page name for primary key, we avoid the extra complexity of generating unique integers. Second, it allows us as students to learn about the two different approaches and how to carry these out in Java web applications.

Setup Logging

Before defining any classes, we should set up the application for logging. Similar to the other application projects, create a lib folder under web/WEB-INF and place the log4j jar file into it. This adds adds log4j to the runtime classpath that the class loader for the web application will search. Additionally, you need to add the log4j jar file to the build path so that the compiler can load log4j class definitions.

Also similar to the other applications, create log4j.properties in the src folder, and add configuration similar to that used in the publisher and website applications.

Page Class

We define a page class in package wiki.data that contains as member variables the column data stored in the database. The class has the following attributes.

   private String name;
   private String content;
   private boolean published;
   private String publishedId;

The page class is simply a container of information; it contains no logic other than that needed to provide access to its data members. Instance of classes such as the page class are sometimes referred to as value objects because that simply represent data without behavior. The complete listing of the page class is as follows.

Page.java

package wiki.data;

public class Page {
	private String name;
	private String content;
	private boolean published;
	private String publishedId;

	public String getContent() {
		return content;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public boolean isPublished() {
		return published;
	}

	public void setPublished(boolean published) {
		this.published = published;
	}

	public String getPublishedId() {
		return publishedId;
	}

	public void setPublishedId(String publishedId) {
		this.publishedId = publishedId;
	}
}

We could have avoided implementing the accessor methods (getters and setters) in the page class by simply declaring the member variables public. However, this is a bad idea for 2 reasons. First, making member variables private is a common coding convention (called the JavaBean convention) followed by the majority of Java programmers, so deviating from the convention makes the code less readable by a wider audience. Second, the choice of accessor name is necessary in order to use the JSP expression language. For example, suppose you are in a jsp file and have a variable called wikiPage that contains a reference to an instance of a wiki page. To write the name of the wiki page into the outgoing stream of HTML, you could simply use the following expression.

${wikiPage.name}

The expression language processes the above expression by capitalizing the n in name to become Name and then it prepends get to this, resulting in the string getName. The expression language then uses this string to get an instance of the getName method of the page class. The ability is provided by what is referred to as the reflection API, which is available in managed execution environments in which virtual machine code runs inside a virtual machine. Systems other than the JSP expression language, such as Hibernate for example, rely on the JavaBean convention for naming getter and setter methods. Thus to avoid interoperability problems with other systems, one should adhere to the convention.

Note that the above exmaple is not really shorter than what is possible without the expression language. For example, the following scriplet is equivalent to the above example.

<%= wikiPage.getName() %>

Data Access Object Super Class

Similar to the publisher application, we avoid duplication of code in the data access object classes by placing common functionality into a parent class called DataAccessObject. The following listing shows the contents of this class.

DataAccessObject.java

package wiki.data;

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

import javax.sql.DataSource;

public class DataAccessObject {
   private static DataSource dataSource;

   public static void setDataSource(DataSource dataSource) 
   {
      DataAccessObject.dataSource = dataSource;
   }

   protected static Connection getConnection() 
   {
      try {
         return dataSource.getConnection();
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }

   protected static void close(Statement statement, Connection connection) 
   {
      close(null, statement, connection);
   }

   protected static void close(ResultSet rs, Statement statement,
         Connection connection) 
   {
      try {
         if (rs != null)
            rs.close();
         if (statement != null)
            statement.close();
         if (connection != null)
            connection.close();
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }
}

The data access object parent class is simpler than the one used for the publisher application because it does not to include code that generates unique integers for use as primary keys.

The Page DAO Class

The following listing shows the contents of the page DAO.

package wiki.data;

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

public class PageDAO extends DataAccessObject {

	private static PageDAO instance = new PageDAO();

	public static PageDAO getInstance() {
		return instance;
	}

	private Page read(ResultSet rs) throws SQLException {
		String name = rs.getString("name");
		String content = rs.getString("content");
		boolean published = rs.getBoolean("published");
		String publishedId = rs.getString("published_id");
		Page page = new Page();
		page.setName(name);
		page.setContent(content);
		page.setPublished(published);
		page.setPublishedId(publishedId);
		return page;
	}

	public Page find(String name) {
		ResultSet rs = null;
		PreparedStatement statement = null;
		Connection connection = null;
		try {
			connection = getConnection();
			String sql = "select * from page where name=?";
			statement = connection.prepareStatement(sql);
			statement.setString(1, name);
			rs = statement.executeQuery();
			if (!rs.next()) {
				return null;
			}
			return read(rs);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			close(rs, statement, connection);
		}
	}

	public void update(Page page) {
		PreparedStatement statement = null;
		Connection connection = null;
		try {
			connection = getConnection();
			String sql = "update page set content=?, published=?, published_id=? where name=?";
			statement = connection.prepareStatement(sql);
			statement.setString(1, page.getContent());
			statement.setBoolean(2, page.isPublished());
			statement.setString(3, page.getPublishedId());
			statement.setString(4, page.getName());
			statement.executeUpdate();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			close(statement, connection);
		}
	}

	public void create(Page page) {
		PreparedStatement statement = null;
		Connection connection = null;
		try {
			connection = getConnection();
                        String sql = "insert into page (name, content) values (?, ?)";
			statement = connection.prepareStatement(sql);
			statement.setString(1, page.getName());
			statement.setString(2, page.getContent());
			statement.executeUpdate();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			close(statement, connection);
		}
	}

	public void delete(Page page) {
		PreparedStatement statement = null;
		Connection connection = null;
		try {
			connection = getConnection();
			String sql = "delete from page where name=?";
			statement = connection.prepareStatement(sql);
			String name = page.getName();
			statement.setString(1, name);
			statement.executeUpdate();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			close(statement, connection);
		}
	}
}

The Init Class

We add the following implementation of the init class in order to establish a reference to the data source that we will configure through the context element. We place this class in the wiki.web package because it is an initialization class that is configured into Tomcat. It could be reasonably argued that this could be alternatively located in the wiki.data package.

package wiki.web;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

import wiki.data.DataAccessObject;

public class Init implements ServletContextListener {

    private Logger logger = Logger.getLogger(this.getClass());
    
    public void contextDestroyed(ServletContextEvent sce) {
    }
    
    private void contextInitialized2(ServletContext servletContext)
    throws Exception {
       InitialContext enc = new InitialContext();
       Context compContext = (Context) enc.lookup("java:comp/env");
       DataSource dataSource = (DataSource) compContext.lookup("datasource");
       DataAccessObject.setDataSource(dataSource);
    }

    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext();
        try {
           contextInitialized2(servletContext);
        }
        catch (Exception e)
        {
           logger.error("Initialization failed.", e);
           throw new RuntimeException(e);
        }
        logger.debug("Initialization succeeded.");
    }
}

If you have build errors, make sure that you have included servlet_api.jar in the project build path.

back next

Copyright 2007-2009 David Turner and Jinseok Chae. All rights reserved.