From 797b589d72ee9f57ed5e306741670366e0976406 Mon Sep 17 00:00:00 2001 From: Vladislav Brown Date: Thu, 23 Jul 2020 19:18:56 +0300 Subject: [PATCH 1/4] fixed code-review remarks --- .../phoneshop/model/product/ArrayListProductDao.java | 11 ++++++----- .../es/phoneshop/model/product/ProductService.java | 2 +- .../phoneshop/model/product/ProductServiceImpl.java | 2 +- .../com/es/phoneshop/web/ProductListPageServlet.java | 2 +- src/main/webapp/WEB-INF/tags/sortFields.tag | 12 ++++++------ .../model/product/ProductServiceImplTest.java | 4 ++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java b/src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java index d114f9c2f..2c62e2043 100644 --- a/src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java +++ b/src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java @@ -37,10 +37,10 @@ private ArrayListProductDao(List products) { public static ArrayListProductDao getInstance() { ArrayListProductDao result = instance; if (result != null) { - return result; + return result; } synchronized (ArrayListProductDao.class) { - if(instance == null){ + if (instance == null) { instance = new ArrayListProductDao(); } return instance; @@ -50,10 +50,10 @@ public static ArrayListProductDao getInstance() { public static ArrayListProductDao getInstance(List products) { ArrayListProductDao result = instance; if (result != null) { - return result; + return result; } synchronized (ArrayListProductDao.class) { - if(instance == null){ + if (instance == null) { instance = new ArrayListProductDao(products); } return instance; @@ -128,9 +128,10 @@ public List find(String query) { String[] terms = query.toLowerCase().split(" "); Arrays.stream(terms) .forEach(term -> comparators.add(this.getDescriptionContainingComparator(term))); + return this.products.stream() - .sorted(((Comparator) ComparatorUtils.chainedComparator(comparators))) .filter(product -> isPartlyContaining(product.getDescription(), terms)) + .sorted(((Comparator) ComparatorUtils.chainedComparator(comparators))) .collect(Collectors.toList()); } else return products; } finally { diff --git a/src/main/java/com/es/phoneshop/model/product/ProductService.java b/src/main/java/com/es/phoneshop/model/product/ProductService.java index 7af651c63..499ac4e2b 100644 --- a/src/main/java/com/es/phoneshop/model/product/ProductService.java +++ b/src/main/java/com/es/phoneshop/model/product/ProductService.java @@ -14,5 +14,5 @@ public interface ProductService { void delete(Long id); - List findProduct(String sort, String order, String query); + List findProducts(String sort, String order, String query); } diff --git a/src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java b/src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java index 16e1e7ec0..cd7d08005 100644 --- a/src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java +++ b/src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java @@ -55,7 +55,7 @@ public void delete(Long id) { //Andrei wanted to use comparators chain but i think it would be overkill @Override - public List findProduct(String sort, String order, String query) { + public List findProducts(String sort, String order, String query) { Comparator comparator = Comparator.comparing(product -> { //also could be used switch with enums diff --git a/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java b/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java index fbaa0b0fd..efc1b9735 100644 --- a/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java +++ b/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java @@ -33,7 +33,7 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp String searchParam = Optional.ofNullable(request.getParameter(String.valueOf(QUERY_PARAM_KEYS.query))).orElse(" "); - request.setAttribute("products", productService.findProduct(sortParam, orderParam, searchParam)); + request.setAttribute("products", productService.findProducts(sortParam, orderParam, searchParam)); request.getRequestDispatcher("/WEB-INF/pages/productList.jsp").forward(request, response); } } diff --git a/src/main/webapp/WEB-INF/tags/sortFields.tag b/src/main/webapp/WEB-INF/tags/sortFields.tag index a05076a51..923fafe63 100644 --- a/src/main/webapp/WEB-INF/tags/sortFields.tag +++ b/src/main/webapp/WEB-INF/tags/sortFields.tag @@ -7,12 +7,12 @@ ${order} \ No newline at end of file diff --git a/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java b/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java index 85e34e289..ec767f95a 100644 --- a/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java +++ b/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java @@ -114,11 +114,11 @@ public void findProductsWithParams() { productDao.set(testList); var expectedList = new ArrayList<>(List.of(validExample1, validExample2)); Collections.reverse(expectedList); - var actual = productService.findProduct(String.valueOf(SortField.description), String.valueOf(SortOrder.asc), " "); + var actual = productService.findProducts(String.valueOf(SortField.description), String.valueOf(SortOrder.asc), " "); assertArrayEquals(expectedList.toArray(), actual.toArray()); Collections.reverse(expectedList); - actual = productService.findProduct(String.valueOf(SortField.description), String.valueOf(SortOrder.desc), " "); + actual = productService.findProducts(String.valueOf(SortField.description), String.valueOf(SortOrder.desc), " "); assertArrayEquals(expectedList.toArray(), actual.toArray()); } } \ No newline at end of file From 383622c5e03586640ad8d7059507ccad29201882 Mon Sep 17 00:00:00 2001 From: VladBrown <45466811+vladikbrown@users.noreply.github.com> Date: Sun, 26 Jul 2020 15:59:34 +0300 Subject: [PATCH 2/4] Task 3.1 implemented cart model --- .../es/phoneshop/model/{product => }/DAO.java | 2 +- .../es/phoneshop/model/cart/entity/Cart.java | 33 +++++++++ .../phoneshop/model/cart/entity/CartItem.java | 30 +++++++++ .../model/cart/service/CartService.java | 12 ++++ .../cart/service/HttpServletCartService.java | 67 +++++++++++++++++++ .../product/ArrayListProductDaoTest.java | 3 + .../model/product/ProductServiceImplTest.java | 7 ++ ...uctDemoDataServletContextListenerTest.java | 8 +-- .../web/ProductDetailsPageServletTest.java | 8 +-- 9 files changed, 159 insertions(+), 11 deletions(-) rename src/main/java/com/es/phoneshop/model/{product => }/DAO.java (88%) create mode 100644 src/main/java/com/es/phoneshop/model/cart/entity/Cart.java create mode 100644 src/main/java/com/es/phoneshop/model/cart/entity/CartItem.java create mode 100644 src/main/java/com/es/phoneshop/model/cart/service/CartService.java create mode 100644 src/main/java/com/es/phoneshop/model/cart/service/HttpServletCartService.java diff --git a/src/main/java/com/es/phoneshop/model/product/DAO.java b/src/main/java/com/es/phoneshop/model/DAO.java similarity index 88% rename from src/main/java/com/es/phoneshop/model/product/DAO.java rename to src/main/java/com/es/phoneshop/model/DAO.java index 91add3b15..25991ebea 100644 --- a/src/main/java/com/es/phoneshop/model/product/DAO.java +++ b/src/main/java/com/es/phoneshop/model/DAO.java @@ -1,4 +1,4 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model; import java.util.List; import java.util.Optional; diff --git a/src/main/java/com/es/phoneshop/model/cart/entity/Cart.java b/src/main/java/com/es/phoneshop/model/cart/entity/Cart.java new file mode 100644 index 000000000..148188d88 --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/cart/entity/Cart.java @@ -0,0 +1,33 @@ +package com.es.phoneshop.model.cart.entity; + +import com.es.phoneshop.model.product.entity.Product; + +import java.util.ArrayList; +import java.util.List; + +public class Cart { + //LinkedHashMap would be better? + //Can Entity-Bean contain some business-logic? + private List cartItems; + + public Cart() { + this.cartItems = new ArrayList<>(); + } + + public List getItems() { + return this.cartItems; + } + + public void add(Product product, int quantity) { + cartItems.add(new CartItem(product, quantity)); + } + + public void add(CartItem cartItem){ + cartItems.add(cartItem); + } + + @Override + public String toString() { + return "Cart[" + cartItems + ']'; + } +} diff --git a/src/main/java/com/es/phoneshop/model/cart/entity/CartItem.java b/src/main/java/com/es/phoneshop/model/cart/entity/CartItem.java new file mode 100644 index 000000000..c576a3f80 --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/cart/entity/CartItem.java @@ -0,0 +1,30 @@ +package com.es.phoneshop.model.cart.entity; + +import com.es.phoneshop.model.product.entity.Product; + +public class CartItem { + private Product product; + + private int quantity; + + public CartItem(Product product, int quantity) { + this.product = product; + this.quantity = quantity; + } + + public Product getProduct() { + return product; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public void increaseQuantity(int quantity){ + this.quantity+=quantity; + } +} diff --git a/src/main/java/com/es/phoneshop/model/cart/service/CartService.java b/src/main/java/com/es/phoneshop/model/cart/service/CartService.java new file mode 100644 index 000000000..eca730a74 --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/cart/service/CartService.java @@ -0,0 +1,12 @@ +package com.es.phoneshop.model.cart.service; + +import com.es.phoneshop.model.cart.entity.Cart; +import com.es.phoneshop.web.exceptions.OutOfStockException; + +public interface CartService { + + Cart getCart(SessionResource sessionResource); + + void add(Cart cart, Long productId, int quantity) throws OutOfStockException; + +} diff --git a/src/main/java/com/es/phoneshop/model/cart/service/HttpServletCartService.java b/src/main/java/com/es/phoneshop/model/cart/service/HttpServletCartService.java new file mode 100644 index 000000000..e9054d04e --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/cart/service/HttpServletCartService.java @@ -0,0 +1,67 @@ +package com.es.phoneshop.model.cart.service; + +import com.es.phoneshop.model.cart.entity.Cart; +import com.es.phoneshop.model.cart.entity.CartItem; +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.dao.ProductDao; +import com.es.phoneshop.model.product.entity.Product; +import com.es.phoneshop.web.exceptions.OutOfStockException; + +import javax.servlet.http.HttpServletRequest; +import java.util.NoSuchElementException; +import java.util.Optional; + +public enum HttpServletCartService implements CartService { + INSTANCE; + + private static final String CART_SESSION_ATTRIBUTE = HttpServletCartService.class.getName() + ".cart"; + + private final ProductDao productDao = ArrayListProductDao.getInstance(); + + @Override + public Cart getCart(HttpServletRequest request) { + //i heard about AtomicReference solution of thread safety problem but it seems to be next level of knowledge + synchronized (request.getSession()) { + Cart cart = (Cart) request.getSession().getAttribute(CART_SESSION_ATTRIBUTE); + if (cart == null) { + request.getSession().setAttribute(CART_SESSION_ATTRIBUTE, cart = new Cart()); + } + return cart; + } + } + + @Override + public void add(Cart cart, Long productId, int quantity) throws OutOfStockException { + //some valid-checking should be here + synchronized (cart) { + try { + Product product = productDao.get(productId).get(); + + if (quantity > product.getStock()) { + throw new OutOfStockException(); + } + + Optional optionalCartItem = findItemInCart(cart, productId); + if (optionalCartItem.isPresent()) { + var cartItem = optionalCartItem.get(); + if (product.getStock() >= cartItem.getQuantity() + quantity) { + cartItem.increaseQuantity(quantity); + } else { + throw new OutOfStockException(); + } + } else { + cart.add(product, quantity); + } + } catch (NoSuchElementException e) { + throw new NoSuchElementException(String.valueOf(productId)); + } + } + } + + private Optional findItemInCart(Cart cart, Long productId) { + return cart.getItems() + .stream() + .filter(existingCartItem -> existingCartItem.getProduct().getId().equals(productId)) + .findFirst(); + } +} diff --git a/src/test/java/com/es/phoneshop/model/product/ArrayListProductDaoTest.java b/src/test/java/com/es/phoneshop/model/product/ArrayListProductDaoTest.java index b8e1cd97e..945582416 100644 --- a/src/test/java/com/es/phoneshop/model/product/ArrayListProductDaoTest.java +++ b/src/test/java/com/es/phoneshop/model/product/ArrayListProductDaoTest.java @@ -1,5 +1,8 @@ package com.es.phoneshop.model.product; +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.dao.TestableSingletonProductDao; +import com.es.phoneshop.model.product.entity.Product; import org.junit.Before; import org.junit.Test; diff --git a/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java b/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java index ec767f95a..88347584d 100644 --- a/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java +++ b/src/test/java/com/es/phoneshop/model/product/ProductServiceImplTest.java @@ -1,6 +1,13 @@ package com.es.phoneshop.model.product; import static org.junit.Assert.*; + +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.dao.TestableSingletonProductDao; +import com.es.phoneshop.model.product.entity.Product; +import com.es.phoneshop.model.product.service.ProductServiceImpl; +import com.es.phoneshop.model.product.sortEnums.SortField; +import com.es.phoneshop.model.product.sortEnums.SortOrder; import org.junit.Before; import org.junit.Test; diff --git a/src/test/java/com/es/phoneshop/web/ProductDemoDataServletContextListenerTest.java b/src/test/java/com/es/phoneshop/web/ProductDemoDataServletContextListenerTest.java index ab8edd17f..c32584fe0 100644 --- a/src/test/java/com/es/phoneshop/web/ProductDemoDataServletContextListenerTest.java +++ b/src/test/java/com/es/phoneshop/web/ProductDemoDataServletContextListenerTest.java @@ -1,10 +1,8 @@ package com.es.phoneshop.web; -import com.es.phoneshop.model.product.ArrayListProductDao; -import com.es.phoneshop.model.product.Product; -import com.es.phoneshop.model.product.ProductDao; -import com.es.phoneshop.model.product.TestableSingletonProductDao; -import org.junit.After; +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.entity.Product; +import com.es.phoneshop.model.product.dao.TestableSingletonProductDao; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/src/test/java/com/es/phoneshop/web/ProductDetailsPageServletTest.java b/src/test/java/com/es/phoneshop/web/ProductDetailsPageServletTest.java index f607a56bc..7e103bb83 100644 --- a/src/test/java/com/es/phoneshop/web/ProductDetailsPageServletTest.java +++ b/src/test/java/com/es/phoneshop/web/ProductDetailsPageServletTest.java @@ -1,9 +1,8 @@ package com.es.phoneshop.web; -import com.es.phoneshop.model.product.ArrayListProductDao; -import com.es.phoneshop.model.product.Product; -import com.es.phoneshop.model.product.TestableSingletonProductDao; -import org.junit.After; +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.entity.Product; +import com.es.phoneshop.model.product.dao.TestableSingletonProductDao; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -13,7 +12,6 @@ import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.awt.image.AreaAveragingScaleFilter; import java.io.IOException; import java.math.BigDecimal; import java.util.Currency; From 96851e625a9cd7287e68b0f4d56488f3a2e561af Mon Sep 17 00:00:00 2001 From: vladbrown Date: Sun, 26 Jul 2020 16:04:01 +0300 Subject: [PATCH 3/4] Tasks 3.2 & 3.3 adding2cart and storing cart in session --- .../es/phoneshop/model/product/SortOrder.java | 5 - .../{ => dao}/ArrayListProductDao.java | 6 +- .../model/product/{ => dao}/ProductDao.java | 5 +- .../TestableSingletonProductDao.java | 4 +- .../product/{ => entity}/PriceHistory.java | 2 +- .../model/product/{ => entity}/Product.java | 2 +- .../product/{ => service}/ProductService.java | 4 +- .../{ => service}/ProductServiceImpl.java | 30 +++-- .../product/{ => sortEnums}/SortField.java | 2 +- .../model/product/sortEnums/SortOrder.java | 5 + ...ProductDemoDataServletContextListener.java | 6 +- .../web/ProductDetailsPageServlet.java | 74 ++++++++++-- .../phoneshop/web/ProductListPageServlet.java | 19 ++-- .../es/phoneshop/web/QUERY_PARAM_KEYS.java | 5 - .../web/exceptions/OutOfStockException.java | 4 + .../web/paramEnums/GetParamKeys.java | 5 + .../web/paramEnums/PostParamKeys.java | 5 + .../webapp/WEB-INF/pages/priceHistoryPage.jsp | 2 +- .../webapp/WEB-INF/pages/productDetails.jsp | 106 +++++++++++++----- src/main/webapp/WEB-INF/pages/productList.jsp | 6 - src/main/webapp/WEB-INF/web.xml | 3 +- src/main/webapp/styles/main.css | 9 +- 22 files changed, 223 insertions(+), 86 deletions(-) delete mode 100644 src/main/java/com/es/phoneshop/model/product/SortOrder.java rename src/main/java/com/es/phoneshop/model/product/{ => dao}/ArrayListProductDao.java (96%) rename src/main/java/com/es/phoneshop/model/product/{ => dao}/ProductDao.java (72%) rename src/main/java/com/es/phoneshop/model/product/{ => dao}/TestableSingletonProductDao.java (61%) rename src/main/java/com/es/phoneshop/model/product/{ => entity}/PriceHistory.java (94%) rename src/main/java/com/es/phoneshop/model/product/{ => entity}/Product.java (98%) rename src/main/java/com/es/phoneshop/model/product/{ => service}/ProductService.java (74%) rename src/main/java/com/es/phoneshop/model/product/{ => service}/ProductServiceImpl.java (74%) rename src/main/java/com/es/phoneshop/model/product/{ => sortEnums}/SortField.java (50%) create mode 100644 src/main/java/com/es/phoneshop/model/product/sortEnums/SortOrder.java delete mode 100644 src/main/java/com/es/phoneshop/web/QUERY_PARAM_KEYS.java create mode 100644 src/main/java/com/es/phoneshop/web/exceptions/OutOfStockException.java create mode 100644 src/main/java/com/es/phoneshop/web/paramEnums/GetParamKeys.java create mode 100644 src/main/java/com/es/phoneshop/web/paramEnums/PostParamKeys.java diff --git a/src/main/java/com/es/phoneshop/model/product/SortOrder.java b/src/main/java/com/es/phoneshop/model/product/SortOrder.java deleted file mode 100644 index 8ea7c4ab5..000000000 --- a/src/main/java/com/es/phoneshop/model/product/SortOrder.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.es.phoneshop.model.product; - -public enum SortOrder { - asc, desc -} diff --git a/src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java b/src/main/java/com/es/phoneshop/model/product/dao/ArrayListProductDao.java similarity index 96% rename from src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java rename to src/main/java/com/es/phoneshop/model/product/dao/ArrayListProductDao.java index 2c62e2043..e9a645a77 100644 --- a/src/main/java/com/es/phoneshop/model/product/ArrayListProductDao.java +++ b/src/main/java/com/es/phoneshop/model/product/dao/ArrayListProductDao.java @@ -1,5 +1,6 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.dao; +import com.es.phoneshop.model.product.entity.Product; import org.apache.commons.collections.ComparatorUtils; import java.util.*; @@ -9,6 +10,7 @@ import java.util.stream.Collectors; public class ArrayListProductDao implements ProductDao, TestableSingletonProductDao> { + //todo implement more elegant singleton :) private static volatile ArrayListProductDao instance; private long maxID; @@ -82,7 +84,6 @@ public List getAll() { } } - //in my humble opinion it is wrong implementation of create/update @Override public void save(Product product) { writeLock.lock(); @@ -168,7 +169,6 @@ private boolean onlySecondContainsTerm(String first, String second, String term) } private boolean areBothContainingTerm(String first, String second, String term) { - //!product1Description.contains(term) && !product2Description.contains(term) || return first.contains(term) && second.contains(term); } diff --git a/src/main/java/com/es/phoneshop/model/product/ProductDao.java b/src/main/java/com/es/phoneshop/model/product/dao/ProductDao.java similarity index 72% rename from src/main/java/com/es/phoneshop/model/product/ProductDao.java rename to src/main/java/com/es/phoneshop/model/product/dao/ProductDao.java index 6cdf88c8f..ea574c739 100644 --- a/src/main/java/com/es/phoneshop/model/product/ProductDao.java +++ b/src/main/java/com/es/phoneshop/model/product/dao/ProductDao.java @@ -1,4 +1,7 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.dao; + +import com.es.phoneshop.model.DAO; +import com.es.phoneshop.model.product.entity.Product; import java.util.List; import java.util.NoSuchElementException; diff --git a/src/main/java/com/es/phoneshop/model/product/TestableSingletonProductDao.java b/src/main/java/com/es/phoneshop/model/product/dao/TestableSingletonProductDao.java similarity index 61% rename from src/main/java/com/es/phoneshop/model/product/TestableSingletonProductDao.java rename to src/main/java/com/es/phoneshop/model/product/dao/TestableSingletonProductDao.java index 6f2b5052c..c639b8a97 100644 --- a/src/main/java/com/es/phoneshop/model/product/TestableSingletonProductDao.java +++ b/src/main/java/com/es/phoneshop/model/product/dao/TestableSingletonProductDao.java @@ -1,4 +1,6 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.dao; + +import com.es.phoneshop.model.product.dao.ProductDao; public interface TestableSingletonProductDao extends ProductDao { Resource get(); diff --git a/src/main/java/com/es/phoneshop/model/product/PriceHistory.java b/src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java similarity index 94% rename from src/main/java/com/es/phoneshop/model/product/PriceHistory.java rename to src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java index 4b7ae067c..44d66a2bd 100644 --- a/src/main/java/com/es/phoneshop/model/product/PriceHistory.java +++ b/src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java @@ -1,4 +1,4 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.entity; import java.math.BigDecimal; import java.util.Currency; diff --git a/src/main/java/com/es/phoneshop/model/product/Product.java b/src/main/java/com/es/phoneshop/model/product/entity/Product.java similarity index 98% rename from src/main/java/com/es/phoneshop/model/product/Product.java rename to src/main/java/com/es/phoneshop/model/product/entity/Product.java index 3b0039c17..90f3f3221 100644 --- a/src/main/java/com/es/phoneshop/model/product/Product.java +++ b/src/main/java/com/es/phoneshop/model/product/entity/Product.java @@ -1,4 +1,4 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.entity; import java.math.BigDecimal; import java.util.ArrayList; diff --git a/src/main/java/com/es/phoneshop/model/product/ProductService.java b/src/main/java/com/es/phoneshop/model/product/service/ProductService.java similarity index 74% rename from src/main/java/com/es/phoneshop/model/product/ProductService.java rename to src/main/java/com/es/phoneshop/model/product/service/ProductService.java index 499ac4e2b..1b9008a62 100644 --- a/src/main/java/com/es/phoneshop/model/product/ProductService.java +++ b/src/main/java/com/es/phoneshop/model/product/service/ProductService.java @@ -1,4 +1,6 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.service; + +import com.es.phoneshop.model.product.entity.Product; import java.util.List; diff --git a/src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java b/src/main/java/com/es/phoneshop/model/product/service/ProductServiceImpl.java similarity index 74% rename from src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java rename to src/main/java/com/es/phoneshop/model/product/service/ProductServiceImpl.java index cd7d08005..e8d8b4d2a 100644 --- a/src/main/java/com/es/phoneshop/model/product/ProductServiceImpl.java +++ b/src/main/java/com/es/phoneshop/model/product/service/ProductServiceImpl.java @@ -1,35 +1,44 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.service; + +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.dao.ProductDao; +import com.es.phoneshop.model.product.entity.Product; +import com.es.phoneshop.model.product.sortEnums.SortField; +import com.es.phoneshop.model.product.sortEnums.SortOrder; import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; +public enum ProductServiceImpl implements ProductService { -public class ProductServiceImpl implements ProductService { - private final ProductDao productDao; + INSTANCE; - public ProductServiceImpl() { - this.productDao = ArrayListProductDao.getInstance(); - } + private final ProductDao productDao = ArrayListProductDao.getInstance(); - //don't know what to return in nonpresent case to provide 404 code :) @Override public Product getProduct(Long id) throws NoSuchElementException { - return productDao.get(id).get(); + try { + return productDao.get(id).get(); + } catch (NoSuchElementException e) { + throw new NoSuchElementException(String.valueOf(id)); + } } @Override public Product getProduct(String pathInfo) throws NoSuchElementException { - long longId; Product result; try { + long longId; longId = Integer.parseInt(pathInfo.split("/")[1]); result = productDao.get(longId).get(); - } catch (NumberFormatException | NoSuchElementException | ArrayIndexOutOfBoundsException e) { + } catch (NumberFormatException | NoSuchElementException e) { //could be created special IncorrectPathInfoException throw new NoSuchElementException(pathInfo.split("/")[1]); + } catch (ArrayIndexOutOfBoundsException e) { + throw new NoSuchElementException(" "); } return result; } @@ -53,7 +62,6 @@ public void delete(Long id) { } - //Andrei wanted to use comparators chain but i think it would be overkill @Override public List findProducts(String sort, String order, String query) { diff --git a/src/main/java/com/es/phoneshop/model/product/SortField.java b/src/main/java/com/es/phoneshop/model/product/sortEnums/SortField.java similarity index 50% rename from src/main/java/com/es/phoneshop/model/product/SortField.java rename to src/main/java/com/es/phoneshop/model/product/sortEnums/SortField.java index 37890a7f3..2d7d81773 100644 --- a/src/main/java/com/es/phoneshop/model/product/SortField.java +++ b/src/main/java/com/es/phoneshop/model/product/sortEnums/SortField.java @@ -1,4 +1,4 @@ -package com.es.phoneshop.model.product; +package com.es.phoneshop.model.product.sortEnums; public enum SortField { description , price diff --git a/src/main/java/com/es/phoneshop/model/product/sortEnums/SortOrder.java b/src/main/java/com/es/phoneshop/model/product/sortEnums/SortOrder.java new file mode 100644 index 000000000..4d44d3620 --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/product/sortEnums/SortOrder.java @@ -0,0 +1,5 @@ +package com.es.phoneshop.model.product.sortEnums; + +public enum SortOrder { + asc, desc +} diff --git a/src/main/java/com/es/phoneshop/web/ProductDemoDataServletContextListener.java b/src/main/java/com/es/phoneshop/web/ProductDemoDataServletContextListener.java index 05d6d4add..dff7ce334 100644 --- a/src/main/java/com/es/phoneshop/web/ProductDemoDataServletContextListener.java +++ b/src/main/java/com/es/phoneshop/web/ProductDemoDataServletContextListener.java @@ -1,8 +1,8 @@ package com.es.phoneshop.web; -import com.es.phoneshop.model.product.ArrayListProductDao; -import com.es.phoneshop.model.product.PriceHistory; -import com.es.phoneshop.model.product.Product; +import com.es.phoneshop.model.product.dao.ArrayListProductDao; +import com.es.phoneshop.model.product.entity.PriceHistory; +import com.es.phoneshop.model.product.entity.Product; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; diff --git a/src/main/java/com/es/phoneshop/web/ProductDetailsPageServlet.java b/src/main/java/com/es/phoneshop/web/ProductDetailsPageServlet.java index c248ff2f7..817ca06ac 100644 --- a/src/main/java/com/es/phoneshop/web/ProductDetailsPageServlet.java +++ b/src/main/java/com/es/phoneshop/web/ProductDetailsPageServlet.java @@ -1,7 +1,13 @@ package com.es.phoneshop.web; -import com.es.phoneshop.model.product.ProductService; -import com.es.phoneshop.model.product.ProductServiceImpl; +import com.es.phoneshop.model.cart.service.CartService; +import com.es.phoneshop.model.cart.service.HttpServletCartService; +import com.es.phoneshop.model.product.service.ProductService; +import com.es.phoneshop.model.product.service.ProductServiceImpl; +import com.es.phoneshop.model.recentlyViewed.HttpServletRecentlyViewedService; +import com.es.phoneshop.model.recentlyViewed.RecentlyViewedService; +import com.es.phoneshop.web.exceptions.OutOfStockException; +import com.es.phoneshop.web.paramEnums.PostParamKeys; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -9,26 +15,80 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.NoSuchElementException; import java.util.Optional; public class ProductDetailsPageServlet extends HttpServlet { private ProductService productService; + private CartService cartService; + private RecentlyViewedService recentlyViewedService; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - productService = new ProductServiceImpl(); + productService = ProductServiceImpl.INSTANCE; + cartService = HttpServletCartService.INSTANCE; + recentlyViewedService = HttpServletRecentlyViewedService.INSTANCE; } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String req = Optional.ofNullable(request.getPathInfo()).orElse(" "); - request.setAttribute("product", productService.getProduct(request.getPathInfo())); - if(req.contains("priceHistory")) { + String pathInto = Optional.ofNullable(request.getPathInfo()) + .orElse(" "); + + var product = productService.getProduct(pathInto); + request.setAttribute("product", product); + recentlyViewedService.add(recentlyViewedService.getList(request), product); + request.setAttribute("cart", cartService.getCart(request)); + request.setAttribute("recentlyViewed", recentlyViewedService.getList(request)); + + if (pathInto.contains("priceHistory")) { request.getRequestDispatcher("/WEB-INF/pages/priceHistoryPage.jsp").forward(request, response); - } else{ + } else { request.getRequestDispatcher("/WEB-INF/pages/productDetails.jsp").forward(request, response); } } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String pathInto = Optional.ofNullable(request.getPathInfo()) + .orElse(" "); + String quantityParam = Optional.ofNullable(request.getParameter(String.valueOf(PostParamKeys.quantity))) + .orElse(" "); + + + //imho it's bad, i'll think how to implement it better + //? maybe move try-catch block into separate methods? + int quantity; + + try { + NumberFormat numberFormat = NumberFormat.getInstance(request.getLocale()); + quantity = numberFormat.parse(quantityParam).intValue(); + } catch (ParseException e) { + response.sendRedirect(request.getContextPath() + "/products/" + parseId(pathInto) + "?error=Not a number"); + return; + } + + try { + cartService.add(cartService.getCart(request), parseId(pathInto), quantity); + } catch (OutOfStockException e) { + response.sendRedirect(request.getContextPath() + "/products/" + parseId(pathInto) + "?error=Not a number"); + return; + } + + response.sendRedirect(request.getContextPath() + "/products/" + parseId(pathInto) + "?message=Added to cart successfully"); + } + + private long parseId(String pathInfo) { + try { + return Integer.parseInt(pathInfo.split("/")[1]); + } catch (NumberFormatException e) { + throw new NoSuchElementException(pathInfo.split("/")[1]); + } catch (ArrayIndexOutOfBoundsException e) { + throw new NoSuchElementException(" "); + } + } } diff --git a/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java b/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java index efc1b9735..ed0b3366d 100644 --- a/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java +++ b/src/main/java/com/es/phoneshop/web/ProductListPageServlet.java @@ -1,7 +1,10 @@ package com.es.phoneshop.web; -import com.es.phoneshop.model.product.ProductService; -import com.es.phoneshop.model.product.ProductServiceImpl; +import com.es.phoneshop.model.product.service.ProductService; +import com.es.phoneshop.model.product.service.ProductServiceImpl; +import com.es.phoneshop.model.recentlyViewed.HttpServletRecentlyViewedService; +import com.es.phoneshop.model.recentlyViewed.RecentlyViewedService; +import com.es.phoneshop.web.paramEnums.GetParamKeys; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -14,11 +17,13 @@ public class ProductListPageServlet extends HttpServlet { //TODO Controller-layer private ProductService productService; + private RecentlyViewedService panelService; @Override public void init(ServletConfig config) throws ServletException { super.init(config); - productService = new ProductServiceImpl(); + productService = ProductServiceImpl.INSTANCE; + panelService = HttpServletRecentlyViewedService.INSTANCE; } @Override @@ -27,13 +32,13 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - //could be used ternary expression to avoid null but i'm not sure if it's good decision - String sortParam = Optional.ofNullable(request.getParameter(String.valueOf(QUERY_PARAM_KEYS.sort))).orElse(" "); - String orderParam = Optional.ofNullable(request.getParameter(String.valueOf(QUERY_PARAM_KEYS.order))).orElse(" "); - String searchParam = Optional.ofNullable(request.getParameter(String.valueOf(QUERY_PARAM_KEYS.query))).orElse(" "); + String sortParam = Optional.ofNullable(request.getParameter(String.valueOf(GetParamKeys.sort))).orElse(" "); + String orderParam = Optional.ofNullable(request.getParameter(String.valueOf(GetParamKeys.order))).orElse(" "); + String searchParam = Optional.ofNullable(request.getParameter(String.valueOf(GetParamKeys.query))).orElse(" "); request.setAttribute("products", productService.findProducts(sortParam, orderParam, searchParam)); + request.setAttribute("recentlyViewed", panelService.getList(request)); request.getRequestDispatcher("/WEB-INF/pages/productList.jsp").forward(request, response); } } diff --git a/src/main/java/com/es/phoneshop/web/QUERY_PARAM_KEYS.java b/src/main/java/com/es/phoneshop/web/QUERY_PARAM_KEYS.java deleted file mode 100644 index 42585acf2..000000000 --- a/src/main/java/com/es/phoneshop/web/QUERY_PARAM_KEYS.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.es.phoneshop.web; - -public enum QUERY_PARAM_KEYS { - query, sort, order -} diff --git a/src/main/java/com/es/phoneshop/web/exceptions/OutOfStockException.java b/src/main/java/com/es/phoneshop/web/exceptions/OutOfStockException.java new file mode 100644 index 000000000..0b437256b --- /dev/null +++ b/src/main/java/com/es/phoneshop/web/exceptions/OutOfStockException.java @@ -0,0 +1,4 @@ +package com.es.phoneshop.web.exceptions; + +public class OutOfStockException extends Exception { +} diff --git a/src/main/java/com/es/phoneshop/web/paramEnums/GetParamKeys.java b/src/main/java/com/es/phoneshop/web/paramEnums/GetParamKeys.java new file mode 100644 index 000000000..92ffecf0e --- /dev/null +++ b/src/main/java/com/es/phoneshop/web/paramEnums/GetParamKeys.java @@ -0,0 +1,5 @@ +package com.es.phoneshop.web.paramEnums; + +public enum GetParamKeys { + query, sort, order +} diff --git a/src/main/java/com/es/phoneshop/web/paramEnums/PostParamKeys.java b/src/main/java/com/es/phoneshop/web/paramEnums/PostParamKeys.java new file mode 100644 index 000000000..e812346a5 --- /dev/null +++ b/src/main/java/com/es/phoneshop/web/paramEnums/PostParamKeys.java @@ -0,0 +1,5 @@ +package com.es.phoneshop.web.paramEnums; + +public enum PostParamKeys { + quantity; +} diff --git a/src/main/webapp/WEB-INF/pages/priceHistoryPage.jsp b/src/main/webapp/WEB-INF/pages/priceHistoryPage.jsp index b750a91eb..2d4f8fa40 100644 --- a/src/main/webapp/WEB-INF/pages/priceHistoryPage.jsp +++ b/src/main/webapp/WEB-INF/pages/priceHistoryPage.jsp @@ -3,7 +3,7 @@ <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> - +

diff --git a/src/main/webapp/WEB-INF/pages/productDetails.jsp b/src/main/webapp/WEB-INF/pages/productDetails.jsp index 93805cf9b..b783e4b2c 100644 --- a/src/main/webapp/WEB-INF/pages/productDetails.jsp +++ b/src/main/webapp/WEB-INF/pages/productDetails.jsp @@ -3,40 +3,86 @@ <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> - + + + + +
+ ${param.message} +
+
+ +
+ ${param.error} +
+
+

+ Cart: +

- ${product.description} + + Cart is empty yet +

- - - - - - - - - - - - - - - - - -
Image - -
code - ${product.code} -
price - - - -
stock - ${product.stock} -
+
+ + + + + + + +
+ ${cartItem.product.description} + + ${cartItem.quantity} +
+
+

+ ${product.description} +

+
+ + + + + + + + + + + + + + + + + + + + + +
Image + +
code + ${product.code} +
price + + + +
stock + ${product.stock} +
quantity + +
+

+ +

+

return to product list diff --git a/src/main/webapp/WEB-INF/pages/productList.jsp b/src/main/webapp/WEB-INF/pages/productList.jsp index 00f8cd211..1013f15b8 100644 --- a/src/main/webapp/WEB-INF/pages/productList.jsp +++ b/src/main/webapp/WEB-INF/pages/productList.jsp @@ -6,12 +6,6 @@ -

Welcome to Expert-Soft training!

diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 66dc90658..2ccd2175c 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -32,6 +32,7 @@ productDetails /products/* + /add-product-to-cart/* @@ -41,7 +42,7 @@ 500 - /WEB-INF/pages/errorProductNotFound.jsp + /WEB-INF/pages/errorDefault.jsp diff --git a/src/main/webapp/styles/main.css b/src/main/webapp/styles/main.css index a961d971e..ae74743fd 100644 --- a/src/main/webapp/styles/main.css +++ b/src/main/webapp/styles/main.css @@ -21,13 +21,20 @@ td { border : 1px solid gray; vertical-align: top; } -.price { +.price, .quantity, .stock{ text-align: right; } img.product-tile { max-width: 64px; } +.success { + color: green; +} + +.error { + color: red; +} /* Page specific styles */ body.product-list { From 737e3dc991cdbb0d3dc68ff6d1bb8c50a8c92929 Mon Sep 17 00:00:00 2001 From: vladbrown Date: Sun, 26 Jul 2020 20:27:37 +0300 Subject: [PATCH 4/4] Task 3.4 implemented recently viewed list storing in session --- .../model/product/entity/PriceHistory.java | 3 +- .../model/product/entity/Product.java | 3 +- .../HttpServletRecentlyViewedService.java | 40 +++++++++++++++++++ .../recentlyViewed/RecentlyViewedService.java | 12 ++++++ .../webapp/WEB-INF/pages/productDetails.jsp | 3 +- src/main/webapp/WEB-INF/pages/productList.jsp | 3 +- .../webapp/WEB-INF/tags/recentlyViewed.tag | 38 ++++++++++++++++++ 7 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/es/phoneshop/model/recentlyViewed/HttpServletRecentlyViewedService.java create mode 100644 src/main/java/com/es/phoneshop/model/recentlyViewed/RecentlyViewedService.java create mode 100644 src/main/webapp/WEB-INF/tags/recentlyViewed.tag diff --git a/src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java b/src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java index 44d66a2bd..3e0b7f17f 100644 --- a/src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java +++ b/src/main/java/com/es/phoneshop/model/product/entity/PriceHistory.java @@ -1,9 +1,10 @@ package com.es.phoneshop.model.product.entity; +import java.io.Serializable; import java.math.BigDecimal; import java.util.Currency; -public class PriceHistory { +public class PriceHistory implements Serializable { private String date; diff --git a/src/main/java/com/es/phoneshop/model/product/entity/Product.java b/src/main/java/com/es/phoneshop/model/product/entity/Product.java index 90f3f3221..c1aedee3a 100644 --- a/src/main/java/com/es/phoneshop/model/product/entity/Product.java +++ b/src/main/java/com/es/phoneshop/model/product/entity/Product.java @@ -1,12 +1,13 @@ package com.es.phoneshop.model.product.entity; +import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Currency; import java.util.List; import java.util.Objects; -public class Product { +public class Product implements Serializable { private Long id; private String code; private String description; diff --git a/src/main/java/com/es/phoneshop/model/recentlyViewed/HttpServletRecentlyViewedService.java b/src/main/java/com/es/phoneshop/model/recentlyViewed/HttpServletRecentlyViewedService.java new file mode 100644 index 000000000..e45b57aef --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/recentlyViewed/HttpServletRecentlyViewedService.java @@ -0,0 +1,40 @@ +package com.es.phoneshop.model.recentlyViewed; + +import com.es.phoneshop.model.product.entity.Product; + +import javax.servlet.http.HttpServletRequest; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public enum HttpServletRecentlyViewedService implements RecentlyViewedService{ + INSTANCE; + + private static final int DEFAULT_NUMBER_OF_PRODUCTS = 3; + + private static final String RECENTLY_VIEWED_SESSION_ATTRIBUTE = HttpServletRecentlyViewedService.class.getName() + ".queue"; + + @Override + public List getList(HttpServletRequest request) { + synchronized (request.getSession()) { + List queue = (LinkedList) request.getSession().getAttribute(RECENTLY_VIEWED_SESSION_ATTRIBUTE); + if (queue == null) { + request.getSession().setAttribute(RECENTLY_VIEWED_SESSION_ATTRIBUTE, queue = new LinkedList<>()); + } + return queue; + } + } + + @Override + public void add(List recentlyViewed, Product product) { + //should i add non null checking? + synchronized ( recentlyViewed) { + if(recentlyViewed.stream().noneMatch(productInQueue -> productInQueue.equals(product))){ + if(recentlyViewed.size() >= DEFAULT_NUMBER_OF_PRODUCTS) { + ((Queue) recentlyViewed).poll(); + } + ((Queue) recentlyViewed).offer(product); + } + } + } +} diff --git a/src/main/java/com/es/phoneshop/model/recentlyViewed/RecentlyViewedService.java b/src/main/java/com/es/phoneshop/model/recentlyViewed/RecentlyViewedService.java new file mode 100644 index 000000000..15dcf8449 --- /dev/null +++ b/src/main/java/com/es/phoneshop/model/recentlyViewed/RecentlyViewedService.java @@ -0,0 +1,12 @@ +package com.es.phoneshop.model.recentlyViewed; + +import com.es.phoneshop.model.product.entity.Product; + +import java.util.List; + +public interface RecentlyViewedService { + + List getList(SessionResource sessionResource); + + void add(List products, Product product); +} diff --git a/src/main/webapp/WEB-INF/pages/productDetails.jsp b/src/main/webapp/WEB-INF/pages/productDetails.jsp index b783e4b2c..cca6f4f60 100644 --- a/src/main/webapp/WEB-INF/pages/productDetails.jsp +++ b/src/main/webapp/WEB-INF/pages/productDetails.jsp @@ -88,4 +88,5 @@ return to product list

-
\ No newline at end of file + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/pages/productList.jsp b/src/main/webapp/WEB-INF/pages/productList.jsp index 1013f15b8..ba91af293 100644 --- a/src/main/webapp/WEB-INF/pages/productList.jsp +++ b/src/main/webapp/WEB-INF/pages/productList.jsp @@ -49,4 +49,5 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tags/recentlyViewed.tag b/src/main/webapp/WEB-INF/tags/recentlyViewed.tag new file mode 100644 index 000000000..b15da1ef2 --- /dev/null +++ b/src/main/webapp/WEB-INF/tags/recentlyViewed.tag @@ -0,0 +1,38 @@ +<%@ tag trimDirectiveWhitespaces="true" %> +<%@ attribute name="recentlyViewed" type="java.util.List" required="true" rtexprvalue="true" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + + Recently Viewed + + +

+ Recently Viewed +

+ + + + + + + + +
+
+ +
+ +
+ + + +
+
+ +