(See the next iteration.)
Introduction
This is my attempt at a simplistic web application just for the sake of practice. I have three main servlets:
CreateDatabaseController
: responsible for creating the database and the table in MySQL,AddPersonController
: responsible for adding a new person to the table.ShowPersonController
: this one is funny. It expectsshow/1/funky
, where1
is the user ID andfunky
is the first name of the user. If you mistypefunky
topunky
, the filter will fix it and redirect toshow/1/funky
. When the URI is in order, it pukes out JSON of the selected person.
Code
AddPersonController.java
package net.coderodde.web.db.fun.controllers;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
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 static net.coderodde.web.db.fun.controllers.DBUtils.close;
/**
* This controller is responsible for creating new persons.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Aug 8, 2017)
*/
@WebServlet(name = "AddPersonController", urlPatterns = {"/add_person"})
public class AddPersonController extends HttpServlet {
/**
* The SQL command for inserting a person.
*/
private static final String INSERT_PERSON_SQL =
"INSERT INTO funny_persons (first_name, last_name, email) VALUES " +
"(?, ?, ?);";
/**
* Handles the HTTP <code>GET</code> method.
*
* @param request the servlet request.
* @param response the servlet response.
* @throws ServletException if a servlet-specific error occurs.
* @throws IOException if an I/O error occurs.
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("Please use the POST method!");
}
}
/**
* Handles the HTTP <code>POST</code> method.
*
* @param request the servlet request.
* @param response the servlet response.
* @throws ServletException if a servlet-specific error occurs.
* @throws IOException if an I/O error occurs.
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
String firstName = request.getParameter("first_name");
String lastName = request.getParameter("last_name");
String email = request.getParameter("email");
if (firstName.isEmpty()) {
out.println("The first name is empty.");
return;
}
if (lastName.isEmpty()) {
out.println("The last name is empty.");
return;
}
if (email.isEmpty()) {
out.println("The email is empty.");
return;
}
MysqlDataSource mysql = DefaultDataSourceCreator.create();
Connection connection = null;
PreparedStatement statement = null;
try {
connection = mysql.getConnection();
statement = connection.prepareStatement(INSERT_PERSON_SQL);
statement.setString(1, firstName);
statement.setString(2, lastName);
statement.setString(3, email);
statement.executeUpdate();
} catch (SQLException ex) {
out.println("Error: " + ex.getMessage());
} finally {
close(null, statement, connection);
}
}
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "This servlet adds new persons to the database.";
}
}
CreateDatabaseController.java
package net.coderodde.web.db.fun.controllers;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
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 static net.coderodde.web.db.fun.controllers.DBUtils.close;
/**
* This controller creates the database if it is not yet created.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Aug 8, 2017)
*/
@WebServlet(name = "CreateDatabaseController", urlPatterns = {"/create"})
public class CreateDatabaseController extends HttpServlet {
/**
* Creates a new database if not already created.
*/
private static final String CREATE_DATABASE_SQL =
"CREATE DATABASE IF NOT EXISTS funny_db;";
/**
* Switches to 'funny_db'.
*/
private static final String USE_DATABASE_SQL = "USE funny_db";
/**
* Creates the table if not already created.
*/
private static final String CREATE_TABLE_SQL =
"CREATE TABLE IF NOT EXISTS funny_persons (\n" +
"id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n" +
"first_name VARCHAR(40) NOT NULL,\n" +
"last_name VARCHAR(40) NOT NULL,\n" +
"email VARCHAR(50) NOT NULL,\n" +
"created TIMESTAMP);";
/**
* If not yet created, this request creates the database and the table.
*
* @param request the servlet request.
* @param response the servlet response.
* @throws ServletException if a servlet-specific error occurs.
* @throws IOException if an I/O error occurs.
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
MysqlDataSource mysql = DefaultDataSourceCreator.create();
Connection connection = null;
PreparedStatement statement = null;
boolean error = false;
try {
connection = mysql.getConnection();
statement = connection.prepareStatement(CREATE_DATABASE_SQL);
statement.execute();
statement = connection.prepareStatement(USE_DATABASE_SQL);
statement.execute();
statement = connection.prepareStatement(CREATE_TABLE_SQL);
statement.execute();
} catch (SQLException ex) {
error = true;
out.println("Error: " + ex.getMessage());
} finally {
close(null, statement, connection);
}
if (!error) {
out.println("Database 'funny_db' is created!");
}
}
}
/**
* Handles the HTTP <code>GET</code> method.
*
* @param request the servlet request.
* @param response the servlet response.
* @throws ServletException if a servlet-specific error occurs.
* @throws IOException if an I/O error occurs.
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP <code>POST</code> method.
*
* @param request the servlet request.
* @param response the servlet response.
* @throws ServletException if a servlet-specific error occurs.
* @throws IOException if an I/O error occurs.
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description.
*/
@Override
public String getServletInfo() {
return "Creates the database and the table.";
}
}
DBUtils.java
package net.coderodde.web.db.fun.controllers;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* This class contains some common utilities for dealing with databases.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Aug 8, 2017)
*/
public final class DBUtils {
/**
* Creates and sets a MySQL data source.
*
* @param user the user name.
* @param password the password.
* @param url the database URL.
* @param port the database port.
* @param serverName the name of the server.
*
* @return the MySQL data source.
*/
public static MysqlDataSource getMysqlDataSource(String user,
String password,
String url,
int port,
String serverName) {
MysqlDataSource dataSource = new MysqlDataSource();
if (user != null) {
dataSource.setUser(user);
}
if (password != null) {
dataSource.setPassword(password);
}
if (url != null) {
dataSource.setURL(url);
}
if (port >= 0) {
dataSource.setPort(port);
}
if (serverName != null) {
dataSource.setServerName(serverName);
}
return dataSource;
}
/**
* Closes a {@code ResultSet}.
*
* @param resultSet the result set to close.
*/
public static void close(ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException ex) {
throw new RuntimeException("Could not close a ResultSet.", ex);
}
}
/**
* Closes a {@code PreparedStatement}.
*
* @param preparedStatement the prepared statement to close.
*/
public static void close(PreparedStatement preparedStatement) {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
} catch (SQLException ex) {
throw new RuntimeException(
"Could not close a PreparedStatement.",
ex);
}
}
/**
* Closes a {@code Connection}.
*
* @param connection the connection to close.
*/
public static void close(Connection connection) {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException ex) {
throw new RuntimeException(
"Could not close a Connection.",
ex);
}
}
/**
* Closes the data base related resources.
*
* @param resultSet the result set to close.
* @param preparedStatement the prepared statement to close.
* @param connection the connection to close.
*/
public static void close(ResultSet resultSet,
PreparedStatement preparedStatement,
Connection connection) {
close(resultSet);
close(preparedStatement);
close(connection);
}
}
DefaultDataSourceCreator.java
package net.coderodde.web.db.fun.controllers;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class DefaultDataSourceCreator {
public static MysqlDataSource create() {
MysqlDataSource mysql =
DBUtils.getMysqlDataSource(
"root",
"your_password",
"jdbc:mysql://localhost:3306/funny_db",
-1,
null);
return mysql;
}
}
MyFilter.java
package net.coderodde.web.db.fun.controllers;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class MyFilter implements javax.servlet.Filter {
private final int id;
private final String firstName;
public MyFilter(int id, String firstName) {
this.id = id;
this.firstName = firstName;
}
@Override
public void init(FilterConfig filterConfig)
throws ServletException {
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
((HttpServletResponse) response)
.sendRedirect("/DBWebFun/show/" + id + "/" + firstName);
}
@Override
public void destroy() {
}
}
ShowPersonController.java
package net.coderodde.web.db.fun.controllers;
import com.google.gson.Gson;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
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 net.coderodde.web.db.fun.model.FunnyPerson;
import static net.coderodde.web.db.fun.controllers.DBUtils.close;
/**
* This controller is responsible for viewing persons.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Aug 8, 2017)
*/
@WebServlet(name = "ShowPersonController", urlPatterns = {"/show/*"})
public class ShowPersonController extends HttpServlet {
private static final String GET_USER_BY_ID_SQL =
"SELECT * FROM funny_persons WHERE id = ?;";
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code>
* methods.
*
* @param request the servlet request.
* @param response the servlet response.
* @throws ServletException if a servlet-specific error occurs.
* @throws IOException if an I/O error occurs.
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
MysqlDataSource mysql = DefaultDataSourceCreator.create();
String path = request.getPathInfo();
if (path.startsWith("/")) {
path = path.substring(1);
}
String[] tokens = path.split("/");
if (tokens.length == 0) {
out.println("At least the user ID is required.");
return;
}
String idString = tokens[0];
int id = -1;
try {
id = Integer.parseInt(idString);
} catch (NumberFormatException ex) {
out.println(idString + ": not an integer.");
return;
}
FunnyPerson person = new FunnyPerson();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = mysql.getConnection();
statement = connection.prepareStatement(GET_USER_BY_ID_SQL);
statement.setInt(1, id);
resultSet = statement.executeQuery();
if (!resultSet.next()) {
close(resultSet, statement, connection);
out.println("{\"status\": \"error\"}");
return;
}
String matchFirstName = null;
if (tokens.length == 2) {
matchFirstName = tokens[1];
}
if (!matchFirstName.equals(resultSet.getString("first_name"))) {
MyFilter myFilter =
new MyFilter(id, resultSet.getString("first_name"));
myFilter.doFilter(request, response, null);
return;
}
person.setId(resultSet.getInt("id"));
person.setFirstName(resultSet.getString("first_name"));
person.setLastName(resultSet.getString("last_name"));
person.setEmail(resultSet.getString("email"));
person.setCreated(resultSet.getDate("created"));
Gson gson = new Gson();
out.println(gson.toJson(person));
} catch (SQLException ex) {
throw new RuntimeException("SQLException thrown.", ex);
} finally {
close(resultSet, statement, connection);
}
}
}
private void handleInvalidRequest(HttpServletRequest request,
HttpServletResponse response) {
}
/**
* Handles the HTTP <code>GET</code> method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP <code>POST</code> method.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
*
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "Shows the user info via ID/first_name";
}
}
FunnyPerson.java
package net.coderodde.web.db.fun.model;
import java.sql.Date;
public final class FunnyPerson {
private int id;
private String firstName;
private String lastName;
private String email;
private Date created;
public int getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public Date getCreated() {
return created;
}
public void setId(int id) {
this.id = id;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setEmail(String email) {
this.email = email;
}
public void setCreated(Date created) {
this.created = created;
}
}
Other
The Maven project is here.
Critique request
Please tell me anything that comes to mind.