Search code examples
javaservletsmodel-view-controllersoc

How to achieve separation of concerns without using any framework but Tomcat + Servlets?


I have a code that works fine. The important parts are as follows:

My model class:

package biz.tugay.sakila.model;
/* User: [email protected] Date: 25/06/15 Time: 12:48 */

public class Actor {

    private long id;
    private String firstName;
    private String lastName;

    // Getters, setters... 
}

My dao class:

package biz.tugay.sakila.dao;
/* User: [email protected] Date: 25/06/15 Time: 12:12 */

import biz.tugay.sakila.model.Actor;

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

public class ActorDao {

    protected static final Connection connection = DBConnector.getConnection();

    public List<Actor> getAllActors() throws SQLException {

        List<Actor> allActors = new ArrayList<Actor>();

        Statement stmt = connection.createStatement();
        String sql = "SELECT * FROM Actor";

        ResultSet rs = stmt.executeQuery(sql);
        while (rs.next()) {
            Actor actor = new Actor();
            actor.setFirstName(rs.getString("first_name"));
            // You get the idea... Setters again..
            allActors.add(actor);
        }

        rs.close();
        stmt.close();

        return allActors;
    }
}

and the DBConnector

package biz.tugay.sakila.dao;
/* User: [email protected] Date: 25/06/15 Time: 12:35 */

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBConnector {

    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql://localhost/sakila";
    static final String USER = "root";
    static final String PASS = "";

    private static Connection connection = null;

    public static final Connection getConnection() {
        if (connection != null) {
            return connection;
        } else {
            try {
                Class.forName(JDBC_DRIVER);
                connection = DriverManager.getConnection(DB_URL, USER, PASS);
                return connection;
            } catch (ClassNotFoundException e) {

            } catch (SQLException e) {

            }
            throw new UnsupportedOperationException();
        }
    }

}

My Servlet class:

package biz.tugay.sakila.servlet;
/* User: [email protected] Date: 26/06/15 Time: 14:31 */

import biz.tugay.sakila.dao.ActorDao;
import biz.tugay.sakila.model.Actor;

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;
import java.sql.SQLException;
import java.util.List;

@WebServlet(urlPatterns = "/actors")
public class ActorServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        ActorDao actorDao = new ActorDao();
        List<Actor> allActors = null;

        try {
            allActors = actorDao.getAllActors();
            req.setAttribute("allActors",allActors);
            req.getRequestDispatcher("/actors.jsp").forward(req, resp);
        } catch (SQLException e) {

        }
    }
}

And /actors.jsp will show an HTML table to the user.

I have made this exercise myself with the sakila sample database MySQL provides.

My question is, without using any framework such as Spring or Struts, how can I achieve a better separation? For example, currently ActorServlet depends on ActorDao concretely, can I fix this, if so how? Also ActorDao depends heavily on DBConnector. For example, I want to be able to create a NoSQL connector and use it, but currently I can not I guess?


Solution

  • First step is to abstract out some interfaces. For example, make ActorDao an interface, move the implementation to ActorDaoImpl or whatever. Create an ActorDaoFactory that hands you an ActorDao which is, under the covers, an ActorDaoImpl, but the servlet doesn't need to know that.

    Second step is more complex... if you want to only use Tomcat, then injection and the like is out, but you can configure Tomcat to create these new interfaces and put them in JNDI. This process is probably too complex to put in an answer here, but the Tomcat documentation on JNDI is really nice. The process basically involves creating a factory, like I advocated above, and then having Tomcat invoke that factory through configuration.

    Once you do this, looking them up from JNDI is as simple as

    // Obtain our environment naming context
    Context initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    
    // Look up our DAO
    ActorDao ad = (ActorDao)envCtx.lookup("dao/actor");
    

    Good luck!