diff --git a/.gitignore b/.gitignore index 6143e53..744cf26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,19 @@ # Compiled class file *.class +out/ # Log file *.log +# IDEA's stuff +.idea/ +*.iml +*.iws + +# Maven +log/ +target/ + # BlueJ files *.ctxt diff --git a/README.md b/README.md index 46bd2f1..f07eaa9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ # SimpleJavaWebApp Simple Java Web App with one servlet and one JSP page + +## App appearance +![Design](https://pp.userapi.com/c824200/v824200962/12353b/iCK31W8iAcU.jpg) + +## Deploy instruction (IDEA Ultimate) +1. In IDEA's settings add Tomcat plugin; +1. Create a new Run configuration based on the local Tomcat server; +1. Add as an artifact exploded war. + +## Hot deploy (IDEA Ultimate) +1. Select exploded WAR; +1. Update classes and resources; + 1. On ‘Update’ action -> Update classes and resources; + 1. On frame deactivation -> Update classes and resources; +1. Run in Debug Mode. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f4c3a16 --- /dev/null +++ b/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + simple-java-web-app + SimpleJavaWebApp + war + 1.0-SNAPSHOT + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + javax.servlet.jsp.jstl + javax.servlet.jsp.jstl-api + 1.2.1 + + + + taglibs + standard + 1.1.2 + + + + io.rest-assured + rest-assured + 3.1.0 + test + + + + + org.mockito + mockito-all + 1.9.5 + test + + + + junit + junit + 4.4 + test + + + + \ No newline at end of file diff --git a/src/main/java/com/vlavik/homework/data/ObjectContainer.java b/src/main/java/com/vlavik/homework/data/ObjectContainer.java new file mode 100644 index 0000000..77d534b --- /dev/null +++ b/src/main/java/com/vlavik/homework/data/ObjectContainer.java @@ -0,0 +1,18 @@ +package com.vlavik.homework.data; + +import java.util.List; + +public interface ObjectContainer { + + List getList(); + + List getOne(); + + List postOne(); + + List putOne(); + + List deleteOne(); + + int getSize(); +} diff --git a/src/main/java/com/vlavik/homework/data/impl/LanguagesContainer.java b/src/main/java/com/vlavik/homework/data/impl/LanguagesContainer.java new file mode 100644 index 0000000..3b5e50e --- /dev/null +++ b/src/main/java/com/vlavik/homework/data/impl/LanguagesContainer.java @@ -0,0 +1,103 @@ +package com.vlavik.homework.data.impl; + +import com.vlavik.homework.data.ObjectContainer; + +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CopyOnWriteArrayList; + +public class LanguagesContainer implements ObjectContainer { + + private static final List OBJECTS = new CopyOnWriteArrayList<>( + Arrays.asList("Java", "Python", "Ruby", "C++", "JavaScript", "C#", + "Assembler", "Go", "Scala", "Kotlin", "Basic", "PHP", + "Perl", "C", "MATLAB", "Fortran", "Pascal", "SQL", + "Prolog", "LabVIEW", "R", "F", "Groovy" + ) + ); + private static final int OBJECTS_AMOUNT = OBJECTS.size(); + + private final Random random = new SecureRandom(); + private List objectsCopy; + + public LanguagesContainer(int listSize) { + this.objectsCopy = fillOutListAtRandom(listSize); + } + + private List fillOutListAtRandom(int listSize) { + if (listSize > OBJECTS_AMOUNT) { + listSize = OBJECTS_AMOUNT; + } + List shuffledList = new CopyOnWriteArrayList<>(OBJECTS); + Collections.shuffle(shuffledList); + return shuffledList.subList(0, listSize); + } + + @Override + public List getList() { + return objectsCopy; + } + + /** + * Inserts a random item and mixes the list. + * + * @return mixed list + */ + @Override + public List getOne() { + objectsCopy.add(getRandomElement()); + Collections.shuffle(objectsCopy); + return objectsCopy; + } + + /** + * Inserts a random item at the head of the list. + * + * @return updated list + */ + @Override + public List postOne() { + objectsCopy.add(0, getRandomElement()); + return objectsCopy; + } + + /** + * Inserts a random item at the tail of the list. + * + * @return updated list + */ + @Override + public List putOne() { + objectsCopy.add(getRandomElement()); + return objectsCopy; + } + + private String getRandomElement() { + return OBJECTS.get(getRandomIndex()); + } + + /** + * Removes a random item from the list. + * + * @return updated list + */ + @Override + public List deleteOne() { + if (!objectsCopy.isEmpty()) { + objectsCopy.remove(getRandomIndex()); + } + return objectsCopy; + } + + private int getRandomIndex() { + return random.nextInt(OBJECTS_AMOUNT); + } + + @Override + public int getSize() { + return objectsCopy.size(); + } +} diff --git a/src/main/java/com/vlavik/homework/servlet/MainApp.java b/src/main/java/com/vlavik/homework/servlet/MainApp.java new file mode 100644 index 0000000..9e19ae7 --- /dev/null +++ b/src/main/java/com/vlavik/homework/servlet/MainApp.java @@ -0,0 +1,79 @@ +package com.vlavik.homework.servlet; + +import com.vlavik.homework.data.ObjectContainer; +import com.vlavik.homework.data.impl.LanguagesContainer; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.util.Objects.isNull; + +public class MainApp extends HttpServlet { + + private final ObjectContainer collection; + private AtomicInteger totalHits = new AtomicInteger(0); + + public MainApp() { + int collectionSize = 10; + this.collection = new LanguagesContainer(collectionSize); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + increaseHits(req, resp); + req.setAttribute("collection", "true".equals(req.getParameter("update")) + ? collection.getOne() + : collection.getList()); + req.setAttribute("listSize", collection.getSize()); + req.setAttribute("hits", totalHits); + RequestDispatcher rd = getServletContext().getRequestDispatcher("/index.jsp"); + rd.forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + collection.postOne(); + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) { + collection.putOne(); + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) { + collection.deleteOne(); + } + + private void increaseHits(HttpServletRequest request, HttpServletResponse response) { + Cookie[] cookies = request.getCookies(); + + if (isNull(cookies)) { + addCookie(response, totalHits.getAndIncrement()); + } + + for (Cookie cookie : cookies) { + if (cookie.getName().equals("HITS")) { + int hits = Integer.parseInt(cookie.getValue()); + if (totalHits.get() < hits) { + totalHits.getAndAdd(hits + 1); + } else { + cookie.setValue(String.valueOf(totalHits.getAndIncrement())); + response.addCookie(cookie); + } + } + } + } + + private void addCookie(HttpServletResponse response, int totalHits) { + Cookie cookie = new Cookie("HITS", String.valueOf(totalHits)); + cookie.setMaxAge(60 * 60 * 24); // expire in 1 day. + response.addCookie(cookie); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..d2efe0d --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,5 @@ +app.title=Simple Java Web App +app.desc=Simple Java Web App with one servlet and one JSP page + +count.total-collection-size=Total number: +count.total-page-views=Total page views: \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..a934d9b --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,17 @@ + + + + + MainApp + com.vlavik.homework.servlet.MainApp + + + + MainApp + + + \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..0c42513 --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,127 @@ +<%@ page language="java" contentType="text/html; charset=US-ASCII" + pageEncoding="US-ASCII"%> + + + + + + + + + + <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + Codestin Search App + + + + + + + + + + + + + + + +
+
+
+

+ + + +

+

+ + + +

+




+ + + ${emp}, + + +
+ + + + + ${listSize} + +



+ +
+ + + + +
+ +
+

Delete button works. But sometimes it needs to be pressed several times to make it work.

+ +
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/js/send-functions.js b/src/main/webapp/js/send-functions.js new file mode 100644 index 0000000..4305647 --- /dev/null +++ b/src/main/webapp/js/send-functions.js @@ -0,0 +1,42 @@ +function sendGet() { + $.ajax({ + type: "GET", + url: '/', + data: { + update: true + }, + success: function () { + location.reload() + } + }); +} + +function sendPost() { + $.ajax({ + type: "POST", + url: '/', + success: function() { + location.reload(); + } + }); +} + +function sendPut() { + $.ajax({ + type: "PUT", + url: '/', + success: function() { + location.reload(); + } + }); +} + +function sendDelete() { + $.ajax({ + type: "DELETE", + url: '/', + success: function() { + location.reload(); + } + }); +} \ No newline at end of file diff --git a/src/test/java/com/vlavik/homework/servlet/MainAppTest.java b/src/test/java/com/vlavik/homework/servlet/MainAppTest.java new file mode 100644 index 0000000..1e8035a --- /dev/null +++ b/src/test/java/com/vlavik/homework/servlet/MainAppTest.java @@ -0,0 +1,81 @@ +package com.vlavik.homework.servlet; + +import com.vlavik.homework.data.ObjectContainer; +import com.vlavik.homework.data.impl.LanguagesContainer; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static io.restassured.RestAssured.given; +import static org.junit.Assert.*; + +public class MainAppTest extends Mockito { + + private ObjectContainer collection; + private MainApp servlet; + private HttpServletRequest request; + private HttpServletResponse response; + + @Before + public void setUp() throws Exception { + servlet = new MainApp(); + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); + collection = new LanguagesContainer(10); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void doGet() { + when(request.getAttribute("listSize")).thenReturn(collection.getSize()); + new MainApp().doPost(request, response); + + assertTrue(request.getAttribute("listSize").equals(10)); + + } + + @Test + public void doGetWithParam() { + given(). + param("update", "true"). + when(). + get("/"). + then(). + statusCode(200); + } + + @Test + public void doPost() { + given(). + when(). + post("/"). + then(). + statusCode(200); + } + + @Test + public void doPut() { + given(). + when(). + put("/"). + then(). + statusCode(200); + } + + @Test + public void doDelete() { + given(). + when(). + delete("/"). + then(). + statusCode(200); + } +} \ No newline at end of file