Manual Dependency Injection in a Web App..

.. example. Or maybe I should say manual Application Context example.

This is the directory layout I have:

pom.xml
<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/maven-v4_0_0.xsd">
 
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>biz.tugay</groupId>
    <artifactId>sform</artifactId>
    <packaging>war</packaging>
 
    <version>1.0-SNAPSHOT</version>
 
    <name>sform</name>
    <url>http://www.tugay.biz</url>
 
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.192</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
 
    <build>
        <finalName>sform</finalName>
    </build>
 
    <properties>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
</project>

web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
 
    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <el-ignored>false</el-ignored>
            <page-encoding>UTF-8</page-encoding>
            <scripting-invalid>true</scripting-invalid>
            <trim-directive-whitespaces>true</trim-directive-whitespaces>
            <default-content-type>text/html</default-content-type>
        </jsp-property-group>
    </jsp-config>
 
    <resource-ref>
        <description>DB Connection</description>
        <res-ref-name>jdbc/studentform</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
 
</web-app>

context.xml
<?xml version='1.0' encoding='utf-8'?>
<Context>
    <Resource name="jdbc/studentform" auth="Container" type="javax.sql.DataSource"
              maxActive="5" maxIdle="5" maxWait="10000"
              username="sa" password="" driverClassName="org.h2.Driver"
              url="jdbc:h2:tcp://localhost:9092/~/h2dbs/studentform"/>
</Context>

StartupListener.java
package biz.tugay.web.listener;
 
import biz.tugay.core.StudentDao;
 
import javax.annotation.Resource;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.sql.DataSource;
 
/**
 * User: Koray Tugay (koray@tugay.biz)
 * Date: 7/31/2016
 * Time: 10:57 PM
 */
 
@WebListener
public class StartupListener implements ServletContextListener {
 
    @Resource(name = "jdbc/studentform")
    private DataSource dataSource;
 
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        final StudentDao studentDao = new StudentDao(dataSource);
        sce.getServletContext().setAttribute("studentDao", studentDao);
    }
 
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
 
}

EncodingFilter.java
package biz.tugay.web.filter;
 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
/**
 * User: Koray Tugay (koray@tugay.biz)
 * Date: 7/31/2016
 * Time: 1:50 PM
 */
 
@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if (httpServletRequest.getCharacterEncoding() == null) {
            httpServletRequest.setCharacterEncoding("utf-8");
        }
        chain.doFilter(request, response);
    }
 
    @Override
    public void destroy() {
    }
 
}

Student.java
package biz.tugay.core;
 
/**
 * User: Koray Tugay (koray@tugay.biz)
 * Date: 7/31/2016
 * Time: 1:16 PM
 */
public class Student {
 
    private String fullname;
 
    public String getFullname() {
        return fullname;
    }
 
    public void setFullname(String fullname) {
        this.fullname = fullname;
    }
}

StudentDao.java
package biz.tugay.core;
 
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
 
/**
 * User: Koray Tugay (koray@tugay.biz)
 * Date: 7/31/2016
 * Time: 1:17 PM
 */
public class StudentDao {
 
    private final DataSource dataSource;
 
    public StudentDao(DataSource dataSource) {
        this.dataSource = dataSource;
    }
 
    public List<Student> getAllStudents() throws SQLException {
        final ArrayList<Student> students = new ArrayList<>();
        final Connection connection = dataSource.getConnection();
        final Statement statement = connection.createStatement();
        final ResultSet resultSet = statement.executeQuery("SELECT * FROM STUDENT");
        while (resultSet.next()) {
            final String fullname = resultSet.getString("FULLNAME");
            final Student student = new Student();
            student.setFullname(fullname);
            students.add(student);
        }
        connection.close();
        return students;
    }
 
    public void addNewStudentWithFullName(String fullname) throws SQLException {
        final Connection connection = dataSource.getConnection();
        final Statement statement = connection.createStatement();
        statement.executeUpdate("INSERT INTO STUDENT(FULLNAME) VALUES ('" + fullname + "')");
        connection.close();
    }
 
}

IndexServlet.java
package biz.tugay.web;
 
import biz.tugay.core.Student;
import biz.tugay.core.StudentDao;
 
import javax.servlet.ServletConfig;
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;
 
/**
 * User: Koray Tugay (koray@tugay.biz)
 * Date: 7/31/2016
 * Time: 1:12 PM
 */
 
@WebServlet(urlPatterns = "/")
public class IndexServlet extends HttpServlet {
 
    private StudentDao studentDao;
 
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        studentDao = (StudentDao) config.getServletContext().getAttribute("studentDao");
    }
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        List<Student> allStudents = null;
        try {
            allStudents = studentDao.getAllStudents();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        req.setAttribute("allStudents", allStudents);
        req.getRequestDispatcher("/WEB-INF/index.jsp").forward(req, resp);
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        try {
            studentDao.addNewStudentWithFullName(req.getParameter("fullname"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        resp.sendRedirect(req.getContextPath() + "/");
    }
 
}

index.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<head>
    <meta charset="utf-8" content="text/html"/>
    <title>Hello World</title>
</head>
<body>
<div>
    <%--@elvariable id="allStudents" type="java.util.List<biz.tugay.core.Student>"--%>
    <c:forEach items="${allStudents}"
               var="student">
        <c:out value="${student.fullname}"/>
        <br/>
    </c:forEach>
</div>
<hr/>
<div>
    <form id="addStudentForm"
          method="post"
          accept-charset="utf-8"
          action="<c:url value=""/>">
        Full name
        <br/>
        <input type="text" name="fullname"/>
        <br/>
        <input type="submit" value="Add new Student!">
    </form>
</div>
</body>

In action:


Notes & Discussion

So what is so special about this? Well here I kind of implemented a manual Dependency Injection I believe. StartupListener is responsible for injecting the DataSource to StudentDao. So, StudentDao does not really ask for the DataSource. Someone has to provide the DataSource to it, and in this case it is provided when the Application is initialized. Then, this class will be available in the ServletContext. IndexServlet has an instance field StudentDao. When the IndexServlet is initialized, it will obtain the Dao from the ServletContext.

We can pass a stub DataSource to StudentDao and test, which is nice. We can easily have different DataSources for test and production environments, which is also nice.