Sans Pareil Technologies, Inc.

Key To Your Business

Lab 8: Filters


Servlet Filters are used to intercept request and/or response to perform pre/post processing of data. Filters may be used to automatically transform input data in the request to some standard format (convert JSON/XML data into object model for instance) and pass along to downstream servlets that handle the request processing. Similarly output produced from a servlet may be transformed by a filter before sending over the socket connection to the client (format JSON/XML, compress data etc).

Filters are pluggable entities and allow for very flexible loosely coupled applications. Filters may be chained together to form a filter chain, with each filter performing exactly one type of operation. Filters may be enabled/disabled easily through configuration allowing for ease of deployment.

In this exercise, we will use two different filters which will perform the following:
  • Log all request parameters and cookies to the standard servlet log channel.
  • Inject a JPA EntityManager instance for use in servlets in the application.

Project

Create a new gradle based project named lab8. In this project we will separate integration tests from unit tests (normal practice in projects). To do so, we will need to add an additional source set that gradle will build and manage for us. In addition, we will add the integration tests to the standard check task in gradle, so that both integration and unit tests will be run as part of the check cycle.

build.gradle

plugins {
  id 'java'
  id 'war'
}

group 'mis283'
sourceCompatibility = 1.8

apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'

repositories {
  mavenCentral()
}

sourceSets {
  integration {
    java.srcDir 'src/integration/java'
    resources.srcDir 'src/integration/resources'
  }
}

gretty {
  httpPort = 8080
  integrationTestTask = 'integration'
}

dependencies {
  compile 'javax.servlet:javax.servlet-api:3.1.0'
  compile 'org.hibernate:hibernate-core:5.2.11.Final'
  testCompile 'junit:junit:4.12'
  testCompile 'org.httpunit:httpunit:1.7.2'
  runtime 'com.h2database:h2:1.4.196'

  integrationCompile configurations.compile
  integrationCompile configurations.testCompile

  integrationRuntime configurations.runtime
  integrationRuntime configurations.testRuntime
}

task integration(type: Test) {
  group = LifecycleBasePlugin.VERIFICATION_GROUP
  description = 'Runs the integration tests.'

  maxHeapSize = '256m'

  testClassesDir = sourceSets.integration.output.classesDir
  classpath = sourceSets.integration.runtimeClasspath

  binResultsDir = file("$buildDir/integration-test-results/binary/integration")

  reports {
    html.destination = "$buildDir/reports/integration-test"
    junitXml.destination = "$buildDir/integration-test-results"
  }

  mustRunAfter tasks.test
}

check.dependsOn integration


Add the persistence configuration file src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="utf-8" ?>
<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="lab8" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>mis283.model.Site</class>
        <class>mis283.model.Article</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:./lab8" />
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>
            <property name="javax.persistence.schema-generation.scripts.create-target" value="sampleCreate.ddl"/>
            <property name="javax.persistence.schema-generation.scripts.drop-target" value="sampleDrop.ddl"/>
            <property name="dialect" value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.show_sql" value="true" />
        </properties>
    </persistence-unit>
</persistence>

Model

We will use the same object model from lab7. Create the package and classes as in lab7.

The repository classes are a bit different. In this lab we will follow the more standard JPA pattern of retrieving an EntityManager instance in client code, and using that instance for all JPA related operations. This reduces the need to work with detached entity instances.

BaseRepository

package mis283.repository;

import mis283.model.EntityWithId;

import javax.persistence.EntityManager;
import java.util.Collection;
import java.util.Optional;

import static java.lang.String.format;

public class BaseRepository<T extends EntityWithId> {

    Collection<T> retrieveAll(final Class<T> t, final EntityManager em) {
        try {
            em.getTransaction().begin();
            return em.createQuery(format("select c from %s c", t.getSimpleName())).getResultList();
        } finally {
            em.getTransaction().commit();
        }
    }

    Optional<T> retrieve(final T entity, final EntityManager em) {
        try {
            em.getTransaction().begin();
            final T t = em.find((Class<T>) entity.getClass(), entity.getId());
            return Optional.ofNullable(t);
        } finally {
            em.getTransaction().commit();
        }
    }

    T save(final T entity, final EntityManager em) {
        try {
            em.getTransaction().begin();
            if (entity.getId() == null) {
                em.persist(entity);
                return entity;
            } else return em.merge(entity);
        } catch (final Throwable t) {
            em.getTransaction().rollback();
            throw t;
        } finally {
            if (em.getTransaction().isActive()) em.getTransaction().commit();
        }
    }

    void delete(final T entity, final EntityManager em) {
        try {
            em.getTransaction().begin();
            em.remove(entity);
        } catch (final Throwable t) {
            em.getTransaction().rollback();
            throw t;
        } finally {
            if (em.getTransaction().isActive()) em.getTransaction().commit();
        }
    }

    public static boolean notEmpty(final String value) {
        return ((value != null) && !value.isEmpty());
    }
}


SiteRepository

package mis283.repository;

import mis283.model.Site;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.Collection;
import java.util.Optional;

public class SiteRepository extends BaseRepository<Site> {
    private static final SiteRepository singleton = new SiteRepository();
    public static SiteRepository getSiteRepository() { return singleton; }

    public Collection<Site> retrieveAll(final EntityManager entityManager) {
        return retrieveAll( Site.class, entityManager );
    }

    public Optional<Site> retrieve(final int id, final EntityManager entityManager) {
        final Site site = new Site();
        site.setId(id);
        return retrieve(site, entityManager);
    }

    public Optional<Site> retrieve(final String name, final EntityManager em) {
        try {
            em.getTransaction().begin();
            final Query query = em.createQuery( "select s from Site s where s.name = :name" );
            query.setParameter( "name", name );
            final Site site = (Site) query.getSingleResult();
            return Optional.ofNullable( site );
        }
        finally {
            em.getTransaction().commit();
        }
    }

    public Site save(final Site entity, final EntityManager em) {
        return super.save( entity, em );
    }

    public void delete(final Integer id, final EntityManager entityManager) {
        final Optional<Site> option = retrieve(id, entityManager);
        if (option.isPresent()) delete(option.get(), entityManager);
    }

    @Override
    public void delete(final Site entity, final EntityManager entityManager) {
        super.delete(entity, entityManager);
    }
}


ArticleRepository

package mis283.repository;

import mis283.model.Article;
import mis283.model.Site;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public class ArticleRepository extends BaseRepository<Article> {
    private static final ArticleRepository singleton = new ArticleRepository();
    public static ArticleRepository getArticleRepository() { return singleton; }

    public Collection<Article> retrieveAll(final EntityManager entityManager) {
        return retrieveAll(Article.class, entityManager);
    }

    public Optional<Article> retrieve(final int id, final EntityManager entityManager) {
        final Article article = new Article();
        article.setId( id );
        return retrieve(article, entityManager);
    }

    public List<Article> retrieve(final Site site, final EntityManager em) {
        try {
            em.getTransaction().begin();
            final Query query = em.createQuery( "select a from Article a where a.site = :site order by a.title" );
            query.setParameter( "site", site );
            return query.getResultList();
        } finally {
            em.getTransaction().commit();
        }
    }

    public List<Article> retrieve(final String title, final EntityManager em) {
        try {
            em.getTransaction().begin();
            final Query query = em.createQuery( "select a from Article a where a.title = :title" );
            query.setParameter( "title", title );
            return query.getResultList();
        } finally {
            em.getTransaction().commit();
        }
    }

    public Article save(final Article entity, final EntityManager em) {
        return super.save(entity, em);
    }

    public void delete(final Integer id, final EntityManager em) {
        final Optional<Article> option = retrieve(id, em);
        if (option.isPresent()) delete(option.get(), em);
    }

    @Override
    public void delete(final Article article, final EntityManager em) {
        super.delete(article, em);
    }
}

Filters

We can now add the filters we will use for this project. We will use two filters - one to log incoming request information, and another to inject a JPA EntityManager instance into any servlets in the application.

LoggingFilter

package mis283.controller;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

import static java.lang.String.format;

@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
    private ServletContext context;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        context = filterConfig.getServletContext();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest req = (HttpServletRequest) request;
        req.getParameterMap().forEach((key,value) ->
                context.log(format("IP: %s - Path: [%s] - Parameter {key: [%s], value: [%s]",
                        req.getRemoteAddr(), req.getPathInfo(), key, value[0])));

        final Cookie[] cookies = req.getCookies();
        if (cookies != null) {
            for (final Cookie cookie : cookies) {
                context.log(format("IP: %s - Path: [%s] - Cookie {name: [%s], value: [%s]",
                        req.getRemoteAddr(), req.getPathInfo(), cookie.getName(), cookie.getValue()));
            }
        }

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {}
}


EntityManagerFilter

package mis283.controller;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

import static java.lang.String.format;

@WebFilter(urlPatterns = {"/site/*", "/article/*"})
public class EntityManagerFilter implements Filter {
    public static final String ENTITY_MANAGER = "entityManager";
    private EntityManagerFactory emf;
    private ServletContext context;

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        context = filterConfig.getServletContext();
        emf = Persistence.createEntityManagerFactory("lab8" );
        context.log(format("Created EntityManagerFactory: %s", emf));
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        EntityManager entityManager = null;

        try {
            entityManager = emf.createEntityManager();
            context.log(format("Created EntityManager: %s", entityManager));
            request.setAttribute(ENTITY_MANAGER, entityManager);
            chain.doFilter(request, response);
        } catch (final Throwable t) {
            throw new ServletException(t);
        } finally {
            if (entityManager != null) {
                if (entityManager.getTransaction().isActive()) entityManager.getTransaction().rollback();
                entityManager.close();
                context.log(format("Closed EntityManager: %s", entityManager));
            }
        }
    }

    @Override
    public void destroy() {
        context.log(format("Closing EntityManagerFactory: %s", emf));
        emf.close();
    }
}

Controller

In this project we will only exercise the Site entity from the web application. We will use a Servlet to route all requests for viewing/editing a site to the appropriate hidden JSP pages. Create the SiteHandler servlet to handle all the business logic.

package mis283.controller;

import mis283.model.Site;

import javax.persistence.EntityManager;
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.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;

import static java.lang.Integer.valueOf;
import static java.lang.String.format;
import static mis283.controller.EntityManagerFilter.ENTITY_MANAGER;
import static mis283.repository.BaseRepository.notEmpty;
import static mis283.repository.SiteRepository.getSiteRepository;

@WebServlet(urlPatterns = "/site/*")
public class SiteHandler extends HttpServlet {
    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
        final String path = req.getPathInfo().substring(1);
        final String[] parts = path.split("/");

        switch (parts[0]) {
            case "view":
                view(valueOf(parts[1]), req, resp);
                break;
            case "list":
                list(req, resp);
                break;
            case "create":
                getServletContext().getRequestDispatcher("/private/siteForm.jsp").forward(req, resp);
                break;
            case "remove":
                remove(valueOf(parts[1]), req, resp);
                break;
            default:
                final Writer writer = resp.getWriter();
                resp.setContentType("text/plain");
                resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                writer.write(format("Path: %s not supported.", path));
        }
    }

    @Override
    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
        final String path = req.getPathInfo();
        if ("/save".equals(path)) {
            save(req, resp);
            return;
        }

        final Writer writer = resp.getWriter();
        resp.setContentType("text/plain");
        resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
        writer.write(format("Path: %s not supported.", path));
    }

    private void view(final int id, final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        final EntityManager em = (EntityManager) request.getAttribute(ENTITY_MANAGER);
        final Optional<Site> option = getSiteRepository().retrieve(id, em);

        if (option.isPresent()) {
            final Collection<Site> sites = new ArrayList<>(1);
            sites.add(option.get());
            displayList(sites, request, response);
        } else {
            final Writer writer = response.getWriter();
            response.setContentType("text/plain");
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            writer.write(format("Site with id: %d not found", id));
        }
    }

    private void remove(final int id, final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        final EntityManager em = (EntityManager) request.getAttribute(ENTITY_MANAGER);
        getSiteRepository().delete(id, em);
        response.sendRedirect(format("%s/site/list", getServletContext().getContextPath()));
    }

    private void list(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        final EntityManager em = (EntityManager) request.getAttribute(ENTITY_MANAGER);
        final Collection<Site> sites = getSiteRepository().retrieveAll(em);
        displayList(sites, request, response);
    }

    private void displayList(final Collection<Site> sites, final HttpServletRequest request,
                             final HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("sites", sites);
        getServletContext().getRequestDispatcher("/private/siteView.jsp").forward(request, response);
    }

    private void save(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        final Site site = createBean(request);
        if (!validate(site)) throw new ServletException("Incomplete site details");

        final EntityManager em = (EntityManager) request.getAttribute(ENTITY_MANAGER);
        getSiteRepository().save(site, em);
        response.sendRedirect(format("%s/site/list", getServletContext().getContextPath()));
    }

    private boolean validate(final Site site) {
        return notEmpty(site.getName()) && site.getUrl() != null;
    }

    private Site createBean(final HttpServletRequest request) throws ServletException {
        final Site site = new Site();

        final String id = request.getParameter("id");
        if (!"0".equals(id)) site.setId(valueOf(id));

        site.setName(request.getParameter("name"));

        try {
            final URL url = new URL(request.getParameter("url"));
            site.setUrl(url);
        } catch (final MalformedURLException e) {
            throw new ServletException(e);
        }

        return site;
    }
}

View

Create a simple index page similar to the following. This really serves no purpose, other than to give us a link to go to a supported internal page in the application.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Web Crawler Database</title>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
  </head>
  <body>
    <a href="site/list">View Sites</a>
  </body>
</html>


private/siteView.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
  <head>
    <title>Sites crawled</title>
     <link rel="stylesheet" type="text/css" href="../styles.css"/>
  </head>
  <body>
    <h2>Site List</h2>
    <table>
      <thead>
      <tr><th>ID</th><th>Name</th><th>URL</th><th>Remove</th></tr>
      </thead>
      <tbody>
      <c:forEach var="site" items="${sites}">
        <tr site="site" id="${site.id}">
          <td id="site_${site.id}"><a href="${pageContext.request.contextPath}/site/edit/${site.id}">${site.id}</a></td>
          <td id="name_${site.id}"><c:out value="${site.name}"/></td>
          <td id="url_${site.id}"><a href="${site.url}">${site.url}</a></td>
          <td><a href="${pageContext.request.contextPath}/site/remove/${site.id}">Remove</a></td>
        </tr>
      </c:forEach>
      </tbody>
    </table>
    <h3><a href="${pageContext.request.contextPath}/site/create">Create Site</a></h3>
  </body>
</html>


private/siteForm.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Create/Edit Site</title>
     <link rel="stylesheet" type="text/css" href="../styles.css"/>
  </head>
  <body>
    <form id="createSite" action="${pageContext.request.contextPath}/site/save" method="post">
      <label class="label">Name</label>
      <input type="text" name="name" />
      <br/>
      <label class="label">URL</label>
      <input type="url" name="url" />
      <br/>
      <input type="hidden" name="id" value="0" />
      <input type="submit" value"Save" />
    </form>
  </body>
</html>

Unit Test

We will use slightly modified versions of the unit test classes from lab7. Since the EntityManagerFactory for the application is managed by a filter, we will use a Datastore class in the unit test suite to mange the datastore. Create a package mis283.repository under the src/test/java directory.

Datastore

package mis283.repository;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Datastore {
    private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("lab8" );
    private static final Datastore singleton = new Datastore();
    public static Datastore getDatastore() { return singleton; }

    private Datastore() {
        Runtime.getRuntime().addShutdownHook( new Thread( () -> emf.close() ) );
    }

    EntityManager getManager() {
        return emf.createEntityManager();
    }
}


SiteRepositoryTest

package mis283.repository;

import mis283.model.Site;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.persistence.EntityManager;
import java.net.URL;
import java.util.Collection;
import java.util.Optional;

import static java.lang.String.format;
import static mis283.repository.Datastore.getDatastore;
import static mis283.repository.SiteRepository.getSiteRepository;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class SiteRepositoryTest {
    private static final String name = "Unit Test Site";
    private static final URL url;
    private static int id = 0;
    private EntityManager entityManager;

    static {
        URL temp = null;

        try {
            temp = new URL("http://test.com/");
        } catch (final Throwable t) {
            throw new RuntimeException(t);
        }

        url = temp;
    }

    @BeforeClass
    public static void create() {
        final EntityManager entityManager = getDatastore().getManager();
        try {
            create(entityManager);
        } finally {
            entityManager.close();
        }
    }

    static void create(final EntityManager entityManager) {
        final Site site = new Site();
        site.setName(name);
        site.setUrl(url);

        final Site result = getSiteRepository().save(site, entityManager);
        assertTrue(result.getId() > 0);
        id = result.getId();
    }

    @Before
    public void before() {
        entityManager = getDatastore().getManager();
    }

    @Test
    public void retrieveAll() {
        final Collection<Site> results = getSiteRepository().retrieveAll(entityManager);
        assertFalse(results.isEmpty());

        boolean found = false;
        final Site test = defaultSite();

        for (final Site site : results) {
            if (test.equals(site)) {
                found = true;
                break;
            }
        }

        assertTrue(found);
    }

    @Test
    public void retrieve() {
        final Optional<Site> optional = getSiteRepository().retrieve(id, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(defaultSite(), optional.get());
    }

    @Test
    public void retrieveByName() {
        final Optional<Site> optional = getSiteRepository().retrieve(name, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(defaultSite(), optional.get());
    }

    @Test
    public void save() {
        final Site site = defaultSite();
        final String modified = format("%s modified", name);
        site.setName(modified);

        getSiteRepository().save(site, entityManager);
        Optional<Site> optional = getSiteRepository().retrieve(id, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(modified, optional.get().getName());

        site.setName(name);
        getSiteRepository().save(site, entityManager);
        optional = getSiteRepository().retrieve(id, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(name, optional.get().getName());
    }

    @After
    public void after() {
        entityManager.close();
    }

    @AfterClass
    public static void delete() {
        final EntityManager entityManager = getDatastore().getManager();
        try {
            delete(entityManager);
        } finally {
            entityManager.close();
        }
    }

    static void delete(final EntityManager entityManager) {
        getSiteRepository().delete(id, entityManager);
        final Optional<Site> optional = getSiteRepository().retrieve(id, entityManager);
        assertFalse(optional.isPresent());
    }

    static Site defaultSite() {
        final Site site = new Site();
        site.setId(id);
        site.setName(name);
        site.setUrl(url);
        return site;
    }
}


ArticleRepositoryTest

package mis283.repository;

import mis283.model.Article;
import mis283.model.Site;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.persistence.EntityManager;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import static java.lang.String.format;
import static mis283.repository.ArticleRepository.getArticleRepository;
import static mis283.repository.Datastore.getDatastore;
import static mis283.repository.SiteRepository.getSiteRepository;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class ArticleRepositoryTest {
    private static final String title = "Unit Test Title";
    private static final String subtitle = "Unit Test Sub Title";
    private static final String description = "Unit Test Description for article.";
    private static int id = 0;
    private static Site site;
    private EntityManager entityManager;

    @BeforeClass
    public static void create() {
        final EntityManager entityManager = getDatastore().getManager();
        try {
            SiteRepositoryTest.create(entityManager);
            site = SiteRepositoryTest.defaultSite();
            final Optional<Site> optional = getSiteRepository().retrieve(site.getId(), entityManager);
            assertTrue(optional.isPresent());
            site = optional.get();

            final Article article = new Article();
            article.setTitle(title);
            article.setSubtitle(subtitle);
            article.setDescription(description);
            article.setSite(Optional.of(site));

            final Article result = getArticleRepository().save(article, entityManager);
            assertTrue(result.getId() > 0);
            id = result.getId();
        } finally {
            entityManager.close();
        }
    }

    @Before
    public void before() {
        entityManager = getDatastore().getManager();
    }

    @Test
    public void retrieveAll() {
        final Collection<Article> results = getArticleRepository().retrieveAll(entityManager);
        assertFalse(results.isEmpty());
        check(results);
    }

    @Test
    public void retrieve() {
        final Optional<Article> optional = getArticleRepository().retrieve(id, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(defaultArticle(), optional.get());
        assertEquals(site, optional.get().getSite().get());
    }

    @Test
    public void retrieveBySite() {
        final List<Article> list = getArticleRepository().retrieve(site, entityManager);
        assertFalse(list.isEmpty());
        check(list);
    }

    @Test
    public void retrieveByTitle() {
        final List<Article> list = getArticleRepository().retrieve(title, entityManager);
        assertFalse(list.isEmpty());
        check(list);
    }

    @Test
    public void save() {
        final Article article = defaultArticle();
        final String modified = format("%s modified", subtitle);
        article.setSubtitle(modified);

        getArticleRepository().save(article, entityManager);
        Optional<Article> optional = getArticleRepository().retrieve(id, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(modified, optional.get().getSubtitle());

        article.setSubtitle(subtitle);
        getArticleRepository().save(article, entityManager);
        optional = getArticleRepository().retrieve(id, entityManager);
        assertTrue(optional.isPresent());
        assertEquals(subtitle, optional.get().getSubtitle());
    }

    @After
    public void after() {
        entityManager.close();
    }

    @AfterClass
    public static void delete() {
        final EntityManager entityManager = getDatastore().getManager();
        try {
            getArticleRepository().delete(id, entityManager);
            final Optional<Article> optional = getArticleRepository().retrieve(id, entityManager);
            assertFalse(optional.isPresent());
            SiteRepositoryTest.delete();
        } finally {
            entityManager.close();
        }
    }

    private void check(final Collection<Article> collection) {
        boolean found = false;
        final Article test = defaultArticle();

        for (final Article article : collection) {
            if (test.getId().equals(article.getId())) {
                found = true;
                break;
            }
        }

        assertTrue(found);
    }

    private static Article defaultArticle() {
        final Article article = new Article();
        article.setId(id);
        article.setTitle(title);
        article.setSubtitle(subtitle);
        article.setDescription(description);
        article.setSite(Optional.of(site));
        return article;
    }
}

Integration Test

We will now set up the integration test suite. We start by creating the new source set that we configured in gradle.

Create a new directory under src named integration (the same name we configured in the gradle file). Create the standard java and resources directories under integration.

Create mis283.SiteTest integration test suite

package mis283;

import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.HTMLElement;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebForm;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import static java.lang.Integer.valueOf;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class SiteTest {
    private static final String name = "test";
    private static final String url = "http://test.com/";
    private static int id;

    @BeforeClass
    public static void create() throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest("http://localhost:8080/lab8/site/create");
        final WebResponse response = wc.getResponse(request);

        final WebForm form = response.getFormWithID("createSite");
        form.setParameter("name", name);
        form.setParameter("url", url);
        final WebResponse page = form.submit();

        final HTMLElement[] elements = page.getElementsWithAttribute("site", "site");
        assertTrue("No new site created", elements.length > 0);
        assertNotNull(elements[0].getAttribute("id"));
        id = valueOf(elements[0].getAttribute("id"));
    }

    @Test
    public void list() throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest("http://localhost:8080/lab8/site/list");
        final WebResponse response = wc.getResponse(request);

        final HTMLElement[] elements = response.getElementsWithAttribute("site", "site");
        assertTrue("No new site created", elements.length > 0);
    }

    @Test
    public void retrieve() throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest(format("http://localhost:8080/lab8/site/view/%d", id));
        final WebResponse response = wc.getResponse(request);

        final HTMLElement[] elements = response.getElementsWithAttribute("site", "site");
        assertTrue("No new site created", elements.length > 0);

        HTMLElement element = response.getElementWithID(format("site_%d", id));
        assertNotNull("Unable to find HTML element for site", element);

        element = response.getElementWithID(format("name_%d", id));
        assertEquals(name, element.getText());

        element = response.getElementWithID(format("url_%d", id));
        assertEquals(url, element.getText());
    }

    @Test
    public void modify() throws Exception {
        final String modified = format("%s modified", name);
        modifyName(modified);
        modifyName(name);
    }

    @Test
    public void addAndVerify() throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest("http://localhost:8080/lab8/site/create");
        final WebResponse response = wc.getResponse(request);

        final String newName = "test2";
        final String newUrl = "http://test2.com/";

        final WebForm form = response.getFormWithID("createSite");
        form.setParameter("name", newName);
        form.setParameter("url", newUrl);
        final WebResponse page = form.submit();

        HTMLElement[] elements = page.getElementsWithAttribute("site", "site");
        assertTrue("No new site created", elements.length > 1);
        assertNotNull(elements[1].getAttribute("id"));
        int newId = valueOf(elements[1].getAttribute("id"));

        final WebRequest removeRequest = new GetMethodWebRequest(format("http://localhost:8080/lab8/site/remove/%d", newId));
        final WebResponse removeResponse = wc.getResponse(removeRequest);
        elements = removeResponse.getElementsWithAttribute("site", "site");
        assertEquals("Site not removed", elements.length, 1);
    }

    @AfterClass
    public static void remove() throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest(format("http://localhost:8080/lab8/site/remove/%d", id));
        final WebResponse response = wc.getResponse(request);

        final HTMLElement[] elements = response.getElementsWithAttribute("site", "site");
        assertEquals("Site not removed", elements.length, 0);
    }

    private void modifyName(final String modified) throws Exception {
        final WebConversation wc = new WebConversation();
        final WebRequest request = new GetMethodWebRequest(format("http://localhost:8080/lab8/site/edit/%d", id));
        final WebResponse response = wc.getResponse(request);

        final WebForm form = response.getFormWithID("createSite");
        form.setParameter("name", modified);
        form.setParameter("url", url);
        final WebResponse page = form.submit();

        final HTMLElement[] elements = page.getElementsWithAttribute("site", "site");
        assertTrue("No new site created", elements.length > 0);
        assertNotNull(elements[0].getAttribute("id"));

        HTMLElement element = page.getElementWithID(format("site_%d", id));
        assertNotNull("Unable to find HTML element for site", element);

        element = page.getElementWithID(format("name_%d", id));
        assertEquals(modified, element.getText());

        element = page.getElementWithID(format("url_%d", id));
        assertEquals(url, element.getText());
    }
}