refactor: Refactored Layers Architecture To Be A Spring Boot Application. Issue #2450 (#2469)

* #2450 1. Refactored Layers Architecture to be a Spring boot app. 2. Changed the xml-based configuration to annotation-based. 3. Used spring's constructor injection to pass around needed objects. 4. Implemented deleteAll() methods for the CakeBakingServiceImpl to be used during testing  5. Implemented a CommandLineRunner to run the spring boot app. And others.

* #2450 added the contents of the etc directory and a README.md

* #2450 made corrections in response to the PR tests

* #2450 made corrections in response to requested changes

* #2450 made corrections in response to requested changes

---------

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
This commit is contained in:
JabezBrew
2023-07-11 12:57:16 +03:00
committed by GitHub
co-authored by Ilkka Seppälä
parent e40923d938
commit edbb59a982
34 changed files with 1023 additions and 1199 deletions
+9 -27
View File
@@ -3,34 +3,31 @@ title: Layers
category: Architectural category: Architectural
language: en language: en
tag: tag:
- Decoupling - Decoupling
--- ---
## Intent ## Intent
Layers is an architectural pattern where software responsibilities are divided among the different Layers is an architectural pattern where software responsibilities are divided among the different
layers of the application. layers of the application.
## Explanation ## Explanation
Real world example Real world example
> Consider a web site displaying decorated cakes for weddings and such. Instead of the web page > Consider a website displaying decorated cakes for weddings and such. Instead of the web page
> directly reaching into the database, it relies on a service to deliver this information. The > directly reaching into the database, it relies on a service to deliver this information. The
> service then queries the data layer to assimilate the needed information. > service then queries the data layer to assimilate the needed information.
In plain words In plain words
> With Layers architectural pattern different concerns reside on separate layers. View layer is > With Layers architectural pattern different concerns reside on separate layers. View layer is
> interested only in rendering, service layer assembles the requested data from various sources, and > interested only in rendering, service layer assembles the requested data from various sources, and
> data layer gets the bits from the data storage. > data layer gets the bits from the data storage.
Wikipedia says Wikipedia says
> In software engineering, multitier architecture (often referred to as n-tier architecture) or > In software engineering, multitier architecture (often referred to as n-tier architecture) or
> multilayered architecture is a clientserver architecture in which presentation, application > multilayered architecture is a clientserver architecture in which presentation, application
> processing, and data management functions are physically separated. > processing, and data management functions are physically separated.
**Programmatic Example** **Programmatic Example**
On the data layer, we keep our cake building blocks. `Cake` consist of layers and topping. On the data layer, we keep our cake building blocks. `Cake` consist of layers and topping.
@@ -38,14 +35,11 @@ On the data layer, we keep our cake building blocks. `Cake` consist of layers an
```java ```java
@Entity @Entity
public class Cake { public class Cake {
@Id @Id
@GeneratedValue @GeneratedValue
private Long id; private Long id;
@OneToOne(cascade = CascadeType.REMOVE) @OneToOne(cascade = CascadeType.REMOVE)
private CakeTopping topping; private CakeTopping topping;
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
private Set<CakeLayer> layers; private Set<CakeLayer> layers;
} }
@@ -55,17 +49,11 @@ The service layer offers `CakeBakingService` for easy access to different aspect
```java ```java
public interface CakeBakingService { public interface CakeBakingService {
void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException; void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException;
List<CakeInfo> getAllCakes(); List<CakeInfo> getAllCakes();
void saveNewTopping(CakeToppingInfo toppingInfo); void saveNewTopping(CakeToppingInfo toppingInfo);
List<CakeToppingInfo> getAvailableToppings(); List<CakeToppingInfo> getAvailableToppings();
void saveNewLayer(CakeLayerInfo layerInfo); void saveNewLayer(CakeLayerInfo layerInfo);
List<CakeLayerInfo> getAvailableLayers(); List<CakeLayerInfo> getAvailableLayers();
} }
``` ```
@@ -74,20 +62,14 @@ On the top we have our `View` responsible of rendering the cakes.
```java ```java
public interface View { public interface View {
void render(); void render();
} }
@Slf4j @Slf4j
public class CakeViewImpl implements View { public class CakeViewImpl implements View {
private final CakeBakingService cakeBakingService; private final CakeBakingService cakeBakingService;
public CakeViewImpl(CakeBakingService cakeBakingService) { public CakeViewImpl(CakeBakingService cakeBakingService) {
this.cakeBakingService = cakeBakingService; this.cakeBakingService = cakeBakingService;
} }
public void render() { public void render() {
cakeBakingService.getAllCakes().forEach(cake -> LOGGER.info(cake.toString())); cakeBakingService.getAllCakes().forEach(cake -> LOGGER.info(cake.toString()));
} }
@@ -108,4 +90,4 @@ Use the Layers architecture when
## Credits ## Credits
* [Pattern Oriented Software Architecture Volume 1: A System of Patterns](https://www.amazon.com/gp/product/0471958697/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0471958697&linkCode=as2&tag=javadesignpat-20&linkId=e3f42d7a2a4cc8c619bbc0136b20dadb) * [Pattern Oriented Software Architecture Volume 1: A System of Patterns](https://www.amazon.com/gp/product/0471958697/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0471958697&linkCode=as2&tag=javadesignpat-20&linkId=e3f42d7a2a4cc8c619bbc0136b20dadb)
+239 -239
View File
@@ -1,256 +1,256 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true" <class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true"> realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="com.iluwatar.layers.dao.CakeDao" project="layers" <interface id="1" language="java" name="com.iluwatar.layers.dao.CakeDao" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeDao.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="289" y="916"/> <position height="-1" width="-1" x="289" y="916"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</interface> </interface>
<class id="2" language="java" name="com.iluwatar.layers.entity.CakeLayer" project="layers" <class id="2" language="java" name="com.iluwatar.layers.entity.CakeLayer" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeLayer.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeLayer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1438" y="826"/> <position height="-1" width="-1" x="1438" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="3" language="java" name="com.iluwatar.layers.view.CakeViewImpl" project="layers" <class id="3" language="java" name="com.iluwatar.layers.view.CakeViewImpl" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="221"/> <position height="-1" width="-1" x="456" y="221"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="4" language="java" name="com.iluwatar.layers.exception.CakeBakingException" project="layers" <class id="4" language="java" name="com.iluwatar.layers.exception.CakeBakingException" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="143" y="502"/> <position height="-1" width="-1" x="143" y="502"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="5" language="java" name="com.iluwatar.layers.service.CakeBakingServiceImpl" project="layers" <class id="5" language="java" name="com.iluwatar.layers.service.CakeBakingServiceImpl" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="694"/> <position height="-1" width="-1" x="456" y="694"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<interface id="6" language="java" name="com.iluwatar.layers.dao.CakeLayerDao" project="layers" <interface id="6" language="java" name="com.iluwatar.layers.dao.CakeLayerDao" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="918"/> <position height="-1" width="-1" x="456" y="918"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</interface> </interface>
<interface id="7" language="java" name="com.iluwatar.layers.view.View" project="layers" <interface id="7" language="java" name="com.iluwatar.layers.view.View" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/View.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/View.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="65"/> <position height="-1" width="-1" x="456" y="65"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</interface> </interface>
<class id="8" language="java" name="com.iluwatar.layers.dto.CakeToppingInfo" project="layers" <class id="8" language="java" name="com.iluwatar.layers.dto.CakeToppingInfo" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="817" y="530"/> <position height="-1" width="-1" x="817" y="530"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="9" language="java" name="com.iluwatar.layers.dto.CakeInfo" project="layers" <class id="9" language="java" name="com.iluwatar.layers.dto.CakeInfo" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeInfo.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeInfo.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="883" y="265"/> <position height="-1" width="-1" x="883" y="265"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<interface id="10" language="java" name="com.iluwatar.layers.dao.CakeToppingDao" project="layers" <interface id="10" language="java" name="com.iluwatar.layers.dao.CakeToppingDao" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="633" y="918"/> <position height="-1" width="-1" x="633" y="918"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</interface> </interface>
<interface id="11" language="java" name="com.iluwatar.layers.service.CakeBakingService" project="layers" <interface id="11" language="java" name="com.iluwatar.layers.service.CakeBakingService" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="456" y="431"/> <position height="-1" width="-1" x="456" y="431"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</interface> </interface>
<class id="12" language="java" name="com.iluwatar.layers.dto.CakeLayerInfo" project="layers" <class id="12" language="java" name="com.iluwatar.layers.dto.CakeLayerInfo" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1055" y="530"/> <position height="-1" width="-1" x="1055" y="530"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="13" language="java" name="com.iluwatar.layers.entity.Cake" project="layers" <class id="13" language="java" name="com.iluwatar.layers.entity.Cake" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/Cake.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/Cake.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1160" y="826"/> <position height="-1" width="-1" x="1160" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<class id="14" language="java" name="com.iluwatar.layers.entity.CakeTopping" project="layers" <class id="14" language="java" name="com.iluwatar.layers.entity.CakeTopping" project="layers"
file="/layers/src/main/java/com/iluwatar/layers/CakeTopping.java" binary="false" corner="BOTTOM_RIGHT"> file="/layers/src/main/java/com/iluwatar/layers/CakeTopping.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="876" y="826"/> <position height="-1" width="-1" x="876" y="826"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</class> </class>
<association id="15"> <association id="15">
<end type="SOURCE" refId="9" navigable="false"> <end type="SOURCE" refId="9" navigable="false">
<attribute id="16" name="cakeToppingInfo"/> <attribute id="16" name="cakeToppingInfo"/>
<multiplicity id="17" minimum="0" maximum="1"/> <multiplicity id="17" minimum="0" maximum="1"/>
</end> </end>
<end type="TARGET" refId="8" navigable="true"/> <end type="TARGET" refId="8" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<dependency id="18"> <dependency id="18">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="10"/> <end type="TARGET" refId="10"/>
</dependency> </dependency>
<association id="19"> <association id="19">
<end type="SOURCE" refId="13" navigable="false"> <end type="SOURCE" refId="13" navigable="false">
<attribute id="20" name="layers"/> <attribute id="20" name="layers"/>
<multiplicity id="21" minimum="0" maximum="2147483647"/> <multiplicity id="21" minimum="0" maximum="2147483647"/>
</end> </end>
<end type="TARGET" refId="2" navigable="true"/> <end type="TARGET" refId="2" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<dependency id="22"> <dependency id="22">
<end type="SOURCE" refId="11"/> <end type="SOURCE" refId="11"/>
<end type="TARGET" refId="4"/> <end type="TARGET" refId="4"/>
</dependency> </dependency>
<realization id="23"> <realization id="23">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="11"/> <end type="TARGET" refId="11"/>
</realization> </realization>
<association id="24"> <association id="24">
<end type="SOURCE" refId="13" navigable="false"> <end type="SOURCE" refId="13" navigable="false">
<attribute id="25" name="topping"/> <attribute id="25" name="topping"/>
<multiplicity id="26" minimum="0" maximum="1"/> <multiplicity id="26" minimum="0" maximum="1"/>
</end> </end>
<end type="TARGET" refId="14" navigable="true"/> <end type="TARGET" refId="14" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<association id="27"> <association id="27">
<end type="SOURCE" refId="2" navigable="false"> <end type="SOURCE" refId="2" navigable="false">
<attribute id="28" name="cake"/> <attribute id="28" name="cake"/>
<multiplicity id="29" minimum="0" maximum="1"/> <multiplicity id="29" minimum="0" maximum="1"/>
</end> </end>
<end type="TARGET" refId="13" navigable="true"/> <end type="TARGET" refId="13" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<dependency id="30"> <dependency id="30">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="14"/> <end type="TARGET" refId="14"/>
</dependency> </dependency>
<dependency id="31"> <dependency id="31">
<end type="SOURCE" refId="11"/> <end type="SOURCE" refId="11"/>
<end type="TARGET" refId="12"/> <end type="TARGET" refId="12"/>
</dependency> </dependency>
<association id="32"> <association id="32">
<end type="SOURCE" refId="3" navigable="false"> <end type="SOURCE" refId="3" navigable="false">
<attribute id="33" name="cakeBakingService"/> <attribute id="33" name="cakeBakingService"/>
<multiplicity id="34" minimum="0" maximum="1"/> <multiplicity id="34" minimum="0" maximum="1"/>
</end> </end>
<end type="TARGET" refId="11" navigable="true"/> <end type="TARGET" refId="11" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<dependency id="35"> <dependency id="35">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/> <end type="TARGET" refId="1"/>
</dependency> </dependency>
<dependency id="36"> <dependency id="36">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="2"/> <end type="TARGET" refId="2"/>
</dependency> </dependency>
<dependency id="37"> <dependency id="37">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/> <end type="TARGET" refId="4"/>
</dependency> </dependency>
<dependency id="38"> <dependency id="38">
<end type="SOURCE" refId="11"/> <end type="SOURCE" refId="11"/>
<end type="TARGET" refId="8"/> <end type="TARGET" refId="8"/>
</dependency> </dependency>
<association id="39"> <association id="39">
<end type="SOURCE" refId="9" navigable="false"> <end type="SOURCE" refId="9" navigable="false">
<attribute id="40" name="cakeLayerInfos"/> <attribute id="40" name="cakeLayerInfos"/>
<multiplicity id="41" minimum="0" maximum="2147483647"/> <multiplicity id="41" minimum="0" maximum="2147483647"/>
</end> </end>
<end type="TARGET" refId="12" navigable="true"/> <end type="TARGET" refId="12" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<realization id="42"> <realization id="42">
<end type="SOURCE" refId="3"/> <end type="SOURCE" refId="3"/>
<end type="TARGET" refId="7"/> <end type="TARGET" refId="7"/>
</realization> </realization>
<dependency id="43"> <dependency id="43">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="12"/> <end type="TARGET" refId="12"/>
</dependency> </dependency>
<dependency id="44"> <dependency id="44">
<end type="SOURCE" refId="11"/> <end type="SOURCE" refId="11"/>
<end type="TARGET" refId="9"/> <end type="TARGET" refId="9"/>
</dependency> </dependency>
<association id="45"> <association id="45">
<end type="SOURCE" refId="14" navigable="false"> <end type="SOURCE" refId="14" navigable="false">
<attribute id="46" name="cake"/> <attribute id="46" name="cake"/>
<multiplicity id="47" minimum="0" maximum="1"/> <multiplicity id="47" minimum="0" maximum="1"/>
</end> </end>
<end type="TARGET" refId="13" navigable="true"/> <end type="TARGET" refId="13" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<dependency id="48"> <dependency id="48">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="6"/> <end type="TARGET" refId="6"/>
</dependency> </dependency>
<dependency id="49"> <dependency id="49">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="8"/> <end type="TARGET" refId="8"/>
</dependency> </dependency>
<dependency id="50"> <dependency id="50">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="9"/> <end type="TARGET" refId="9"/>
</dependency> </dependency>
<dependency id="51"> <dependency id="51">
<end type="SOURCE" refId="5"/> <end type="SOURCE" refId="5"/>
<end type="TARGET" refId="13"/> <end type="TARGET" refId="13"/>
</dependency> </dependency>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="true" signature="true" <classifier-display autosize="true" stereotype="true" package="true" initial-value="true" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display> </classifier-display>
<association-display labels="true" multiplicity="true"/> <association-display labels="true" multiplicity="true"/>
</class-diagram> </class-diagram>
+2 -2
View File
@@ -131,6 +131,6 @@ CakeViewImpl --> "-cakeBakingService" CakeBakingService
App --> "-cakeBakingService" CakeBakingService App --> "-cakeBakingService" CakeBakingService
Cake --> "-topping" CakeTopping Cake --> "-topping" CakeTopping
CakeLayer --> "-cake" Cake CakeLayer --> "-cake" Cake
CakeBakingServiceImpl ..|> CakeBakingService CakeBakingServiceImpl ..|> CakeBakingService
CakeViewImpl ..|> View CakeViewImpl ..|> View
@enduml @enduml
+57 -64
View File
@@ -1,11 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License The MIT License
Copyright © 2014-2022 Ilkka Seppälä Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
@@ -22,67 +19,63 @@
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.-->
--> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.iluwatar</groupId> <groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId> <artifactId>layers</artifactId>
<version>1.26.0-SNAPSHOT</version> <name>layers</name>
</parent> <description>layers</description>
<groupId>com.iluwatar.layers</groupId> <dependencies>
<artifactId>layers</artifactId> <dependency>
<dependencies> <groupId>org.springframework.boot</groupId>
<dependency> <artifactId>spring-boot-starter</artifactId>
<groupId>org.springframework.data</groupId> </dependency>
<artifactId>spring-data-jpa</artifactId> <dependency>
</dependency> <groupId>org.springframework.boot</groupId>
<dependency> <artifactId>spring-boot-starter-data-jpa</artifactId>
<groupId>org.hibernate</groupId> </dependency>
<artifactId>hibernate-core</artifactId>
</dependency> <dependency>
<dependency> <groupId>com.h2database</groupId>
<groupId>javax.xml.bind</groupId> <artifactId>h2</artifactId>
<artifactId>jaxb-api</artifactId> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-dbcp</groupId> <groupId>org.projectlombok</groupId>
<artifactId>commons-dbcp</artifactId> <artifactId>lombok</artifactId>
</dependency> <optional>true</optional>
<dependency> </dependency>
<groupId>com.h2database</groupId> <dependency>
<artifactId>h2</artifactId> <groupId>org.springframework.boot</groupId>
</dependency> <artifactId>spring-boot-starter-test</artifactId>
<dependency> <scope>test</scope>
<groupId>org.junit.jupiter</groupId> </dependency>
<artifactId>junit-jupiter-engine</artifactId> </dependencies>
<scope>test</scope>
</dependency> <build>
<dependency> <plugins>
<groupId>org.mockito</groupId> <plugin>
<artifactId>mockito-core</artifactId> <groupId>org.springframework.boot</groupId>
<scope>test</scope> <artifactId>spring-boot-maven-plugin</artifactId>
</dependency> <configuration>
</dependencies> <excludes>
<build> <exclude>
<plugins> <groupId>org.projectlombok</groupId>
<plugin> <artifactId>lombok</artifactId>
<groupId>org.apache.maven.plugins</groupId> </exclude>
<artifactId>maven-assembly-plugin</artifactId> </excludes>
<executions> </configuration>
<execution> </plugin>
<configuration> </plugins>
<archive> </build>
<manifest>
<mainClass>com.iluwatar.layers.app.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>
@@ -0,0 +1,68 @@
package com.iluwatar.layers;
import dto.CakeInfo;
import dto.CakeLayerInfo;
import dto.CakeToppingInfo;
import exception.CakeBakingException;
import lombok.extern.slf4j.Slf4j;
import service.CakeBakingService;
import view.CakeViewImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class Runner implements CommandLineRunner {
private final CakeBakingService cakeBakingService;
public static final String STRAWBERRY = "strawberry";
@Autowired
public Runner(CakeBakingService cakeBakingService) {
this.cakeBakingService = cakeBakingService;
}
@Override
public void run(String... args) {
//initialize sample data
initializeData();
// create view and render it
var cakeView = new CakeViewImpl(cakeBakingService);
cakeView.render();
}
/**
* Initializes the example data.
*/
private void initializeData() {
cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200));
cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900));
cakeBakingService.saveNewLayer(new CakeLayerInfo(STRAWBERRY, 950));
cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950));
cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950));
cakeBakingService.saveNewLayer(new CakeLayerInfo(STRAWBERRY, 950));
cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350));
cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350));
var cake1 = new CakeInfo(new CakeToppingInfo("candies", 0), List.of(
new CakeLayerInfo("chocolate", 0),
new CakeLayerInfo("banana", 0),
new CakeLayerInfo(STRAWBERRY, 0)));
try {
cakeBakingService.bakeNewCake(cake1);
} catch (CakeBakingException e) {
LOGGER.error("Cake baking exception", e);
}
var cake2 = new CakeInfo(new CakeToppingInfo("cherry", 0), List.of(
new CakeLayerInfo("vanilla", 0),
new CakeLayerInfo("lemon", 0),
new CakeLayerInfo(STRAWBERRY, 0)));
try {
cakeBakingService.bakeNewCake(cake2);
} catch (CakeBakingException e) {
LOGGER.error("Cake baking exception", e);
}
}
}
@@ -1,135 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.layers.app;
import com.iluwatar.layers.dao.CakeDao;
import com.iluwatar.layers.dao.CakeLayerDao;
import com.iluwatar.layers.dao.CakeToppingDao;
import com.iluwatar.layers.dto.CakeInfo;
import com.iluwatar.layers.dto.CakeLayerInfo;
import com.iluwatar.layers.dto.CakeToppingInfo;
import com.iluwatar.layers.entity.Cake;
import com.iluwatar.layers.entity.CakeLayer;
import com.iluwatar.layers.entity.CakeTopping;
import com.iluwatar.layers.exception.CakeBakingException;
import com.iluwatar.layers.service.CakeBakingService;
import com.iluwatar.layers.service.CakeBakingServiceImpl;
import com.iluwatar.layers.view.CakeViewImpl;
import java.util.List;
/**
* Layers is an architectural style where software responsibilities are divided among the
* different layers of the application.
*
* <p>This example demonstrates a traditional 3-layer architecture consisting of data access
* layer, business layer and presentation layer.
*
* <p>The data access layer is formed of Spring Data repositories <code>CakeDao</code>,
* <code>CakeToppingDao</code> and <code>CakeLayerDao</code>. The repositories can be used
* for CRUD operations on cakes, cake toppings and cake layers respectively.
*
* <p>The business layer is built on top of the data access layer. <code>CakeBakingService</code>
* offers methods to retrieve available cake toppings and cake layers and baked cakes. Also the
* service is used to create new cakes out of cake toppings and cake layers.
*
* <p>The presentation layer is built on the business layer and in this example it simply lists
* the cakes that have been baked.
*
* <p>We have applied so called strict layering which means that the layers can only access the
* classes directly beneath them. This leads the solution to create an additional set of DTOs
* ( <code>CakeInfo</code>, <code>CakeToppingInfo</code>, <code>CakeLayerInfo</code>) to translate
* data between layers. In other words, <code>CakeBakingService</code> cannot return entities
* ( <code>Cake</code>, <code>CakeTopping</code>, <code>CakeLayer</code>) directly since these
* reside on data access layer but instead translates these into business layer DTOs
* (<code>CakeInfo</code>, <code>CakeToppingInfo</code>, <code>CakeLayerInfo</code>) and returns
* them instead. This way the presentation layer does not have any knowledge of other layers than
* the business layer and thus is not affected by changes to them.
*
* @see Cake
* @see CakeTopping
* @see CakeLayer
* @see CakeDao
* @see CakeToppingDao
* @see CakeLayerDao
* @see CakeBakingService
* @see CakeInfo
* @see CakeToppingInfo
* @see CakeLayerInfo
*
*/
public class App {
private static final CakeBakingService cakeBakingService = new CakeBakingServiceImpl();
public static final String STRAWBERRY = "strawberry";
/**
* Application entry point.
*
* @param args Command line parameters
*/
public static void main(String[] args) {
// initialize example data
initializeData(cakeBakingService);
// create view and render it
var cakeView = new CakeViewImpl(cakeBakingService);
cakeView.render();
}
/**
* Initializes the example data.
*/
private static void initializeData(CakeBakingService cakeBakingService) {
cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200));
cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900));
cakeBakingService.saveNewLayer(new CakeLayerInfo(STRAWBERRY, 950));
cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950));
cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950));
cakeBakingService.saveNewLayer(new CakeLayerInfo(STRAWBERRY, 950));
cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350));
cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350));
var cake1 = new CakeInfo(new CakeToppingInfo("candies", 0), List.of(
new CakeLayerInfo("chocolate", 0),
new CakeLayerInfo("banana", 0),
new CakeLayerInfo(STRAWBERRY, 0)));
try {
cakeBakingService.bakeNewCake(cake1);
} catch (CakeBakingException e) {
e.printStackTrace();
}
var cake2 = new CakeInfo(new CakeToppingInfo("cherry", 0), List.of(
new CakeLayerInfo("vanilla", 0),
new CakeLayerInfo("lemon", 0),
new CakeLayerInfo(STRAWBERRY, 0)));
try {
cakeBakingService.bakeNewCake(cake2);
} catch (CakeBakingException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,20 @@
package com.iluwatar.layers.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories(basePackages = "dao")
@EntityScan(basePackages = "entity")
@ComponentScan(basePackages = {"com.iluwatar.layers", "service", "dto", "exception", "view" ,"dao"})
public class LayersApp {
public static void main(String[] args) {
SpringApplication.run(LayersApp.class, args);
}
}
@@ -1,94 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.layers.entity;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
/**
* CakeLayer entity.
*/
@Entity
public class CakeLayer {
@Id
@GeneratedValue
private Long id;
private String name;
private int calories;
@ManyToOne(cascade = CascadeType.ALL)
private Cake cake;
public CakeLayer() {
}
public CakeLayer(String name, int calories) {
this.setName(name);
this.setCalories(calories);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCalories() {
return calories;
}
public void setCalories(int calories) {
this.calories = calories;
}
@Override
public String toString() {
return String.format("id=%s name=%s calories=%d", id, name, calories);
}
public Cake getCake() {
return cake;
}
public void setCake(Cake cake) {
this.cake = cake;
}
}
@@ -1,94 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.layers.entity;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
/**
* CakeTopping entity.
*/
@Entity
public class CakeTopping {
@Id
@GeneratedValue
private Long id;
private String name;
private int calories;
@OneToOne(cascade = CascadeType.ALL)
private Cake cake;
public CakeTopping() {
}
public CakeTopping(String name, int calories) {
this.setName(name);
this.setCalories(calories);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCalories() {
return calories;
}
public void setCalories(int calories) {
this.calories = calories;
}
@Override
public String toString() {
return String.format("id=%s name=%s calories=%d", id, name, calories);
}
public Cake getCake() {
return cake;
}
public void setCake(Cake cake) {
this.cake = cake;
}
}
@@ -1,175 +0,0 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.layers.service;
import com.iluwatar.layers.dao.CakeDao;
import com.iluwatar.layers.dao.CakeLayerDao;
import com.iluwatar.layers.dao.CakeToppingDao;
import com.iluwatar.layers.dto.CakeInfo;
import com.iluwatar.layers.dto.CakeLayerInfo;
import com.iluwatar.layers.dto.CakeToppingInfo;
import com.iluwatar.layers.entity.Cake;
import com.iluwatar.layers.entity.CakeLayer;
import com.iluwatar.layers.entity.CakeTopping;
import com.iluwatar.layers.exception.CakeBakingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Implementation of CakeBakingService.
*/
@Service
@Transactional
public class CakeBakingServiceImpl implements CakeBakingService {
private final AbstractApplicationContext context;
public CakeBakingServiceImpl() {
this.context = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Override
public void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException {
var allToppings = getAvailableToppingEntities();
var matchingToppings =
allToppings.stream().filter(t -> t.getName().equals(cakeInfo.cakeToppingInfo.name))
.toList();
if (matchingToppings.isEmpty()) {
throw new CakeBakingException(String.format("Topping %s is not available",
cakeInfo.cakeToppingInfo.name));
}
var allLayers = getAvailableLayerEntities();
Set<CakeLayer> foundLayers = new HashSet<>();
for (var info : cakeInfo.cakeLayerInfos) {
var found = allLayers.stream().filter(layer -> layer.getName().equals(info.name)).findFirst();
if (found.isEmpty()) {
throw new CakeBakingException(String.format("Layer %s is not available", info.name));
} else {
foundLayers.add(found.get());
}
}
var toppingBean = context.getBean(CakeToppingDao.class);
var topping = toppingBean.findById(matchingToppings.iterator().next().getId());
var cakeBean = context.getBean(CakeDao.class);
if (topping.isPresent()) {
var cake = new Cake();
cake.setTopping(topping.get());
cake.setLayers(foundLayers);
cakeBean.save(cake);
topping.get().setCake(cake);
toppingBean.save(topping.get());
var layerBean = context.getBean(CakeLayerDao.class);
for (var layer : foundLayers) {
layer.setCake(cake);
layerBean.save(layer);
}
} else {
throw new CakeBakingException(String.format("Topping %s is not available",
cakeInfo.cakeToppingInfo.name));
}
}
@Override
public void saveNewTopping(CakeToppingInfo toppingInfo) {
var bean = context.getBean(CakeToppingDao.class);
bean.save(new CakeTopping(toppingInfo.name, toppingInfo.calories));
}
@Override
public void saveNewLayer(CakeLayerInfo layerInfo) {
var bean = context.getBean(CakeLayerDao.class);
bean.save(new CakeLayer(layerInfo.name, layerInfo.calories));
}
private List<CakeTopping> getAvailableToppingEntities() {
var bean = context.getBean(CakeToppingDao.class);
List<CakeTopping> result = new ArrayList<>();
for (CakeTopping topping : bean.findAll()) {
if (topping.getCake() == null) {
result.add(topping);
}
}
return result;
}
@Override
public List<CakeToppingInfo> getAvailableToppings() {
var bean = context.getBean(CakeToppingDao.class);
List<CakeToppingInfo> result = new ArrayList<>();
for (CakeTopping next : bean.findAll()) {
if (next.getCake() == null) {
result.add(new CakeToppingInfo(next.getId(), next.getName(), next.getCalories()));
}
}
return result;
}
private List<CakeLayer> getAvailableLayerEntities() {
var bean = context.getBean(CakeLayerDao.class);
List<CakeLayer> result = new ArrayList<>();
for (CakeLayer next : bean.findAll()) {
if (next.getCake() == null) {
result.add(next);
}
}
return result;
}
@Override
public List<CakeLayerInfo> getAvailableLayers() {
var bean = context.getBean(CakeLayerDao.class);
List<CakeLayerInfo> result = new ArrayList<>();
for (CakeLayer next : bean.findAll()) {
if (next.getCake() == null) {
result.add(new CakeLayerInfo(next.getId(), next.getName(), next.getCalories()));
}
}
return result;
}
@Override
public List<CakeInfo> getAllCakes() {
var cakeBean = context.getBean(CakeDao.class);
List<CakeInfo> result = new ArrayList<>();
for (Cake cake : cakeBean.findAll()) {
var cakeToppingInfo =
new CakeToppingInfo(cake.getTopping().getId(), cake.getTopping().getName(), cake
.getTopping().getCalories());
List<CakeLayerInfo> cakeLayerInfos = new ArrayList<>();
for (var layer : cake.getLayers()) {
cakeLayerInfos.add(new CakeLayerInfo(layer.getId(), layer.getName(), layer.getCalories()));
}
var cakeInfo = new CakeInfo(cake.getId(), cakeToppingInfo, cakeLayerInfos);
result.add(cakeInfo);
}
return result;
}
}
@@ -22,16 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.dao; package dao;
import com.iluwatar.layers.entity.Cake; import entity.Cake;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
/** /**
* CRUD repository for cakes. * CRUD repository for cakes.
*/ */
@Repository @Repository
public interface CakeDao extends CrudRepository<Cake, Long> { public interface CakeDao extends JpaRepository<Cake, Long> {}
}
@@ -22,16 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.dao; package dao;
import com.iluwatar.layers.entity.CakeLayer; import entity.CakeLayer;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
/** /**
* CRUD repository for cake layers. * CRUD repository for cake layers.
*/ */
@Repository @Repository
public interface CakeLayerDao extends CrudRepository<CakeLayer, Long> { public interface CakeLayerDao extends JpaRepository<CakeLayer, Long> {
} }
@@ -22,16 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.dao; package dao;
import com.iluwatar.layers.entity.CakeTopping;
import org.springframework.data.repository.CrudRepository;
import entity.CakeTopping;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
/** /**
* CRUD repository cake toppings. * CRUD repository cake toppings.
*/ */
@Repository @Repository
public interface CakeToppingDao extends CrudRepository<CakeTopping, Long> { public interface CakeToppingDao extends JpaRepository<CakeTopping, Long> {
} }
@@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.dto; package dto;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -32,40 +32,40 @@ import java.util.Optional;
*/ */
public class CakeInfo { public class CakeInfo {
public final Optional<Long> id; public final Optional<Long> id;
public final CakeToppingInfo cakeToppingInfo; public final CakeToppingInfo cakeToppingInfo;
public final List<CakeLayerInfo> cakeLayerInfos; public final List<CakeLayerInfo> cakeLayerInfos;
/** /**
* Constructor. * Constructor.
*/ */
public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List<CakeLayerInfo> cakeLayerInfos) { public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List<CakeLayerInfo> cakeLayerInfos) {
this.id = Optional.of(id); this.id = Optional.of(id);
this.cakeToppingInfo = cakeToppingInfo; this.cakeToppingInfo = cakeToppingInfo;
this.cakeLayerInfos = cakeLayerInfos; this.cakeLayerInfos = cakeLayerInfos;
} }
/** /**
* Constructor. * Constructor.
*/ */
public CakeInfo(CakeToppingInfo cakeToppingInfo, List<CakeLayerInfo> cakeLayerInfos) { public CakeInfo(CakeToppingInfo cakeToppingInfo, List<CakeLayerInfo> cakeLayerInfos) {
this.id = Optional.empty(); this.id = Optional.empty();
this.cakeToppingInfo = cakeToppingInfo; this.cakeToppingInfo = cakeToppingInfo;
this.cakeLayerInfos = cakeLayerInfos; this.cakeLayerInfos = cakeLayerInfos;
} }
/** /**
* Calculate calories. * Calculate calories.
*/ */
public int calculateTotalCalories() { public int calculateTotalCalories() {
var total = cakeToppingInfo != null ? cakeToppingInfo.calories : 0; var total = cakeToppingInfo != null ? cakeToppingInfo.calories : 0;
total += cakeLayerInfos.stream().mapToInt(c -> c.calories).sum(); total += cakeLayerInfos.stream().mapToInt(c -> c.calories).sum();
return total; return total;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("CakeInfo id=%d topping=%s layers=%s totalCalories=%d", id.orElse(-1L), return String.format("CakeInfo id=%d topping=%s layers=%s totalCalories=%d", id.orElse(-1L),
cakeToppingInfo, cakeLayerInfos, calculateTotalCalories()); cakeToppingInfo, cakeLayerInfos, calculateTotalCalories());
} }
} }
@@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.dto; package dto;
import java.util.Optional; import java.util.Optional;
@@ -31,30 +31,30 @@ import java.util.Optional;
*/ */
public class CakeLayerInfo { public class CakeLayerInfo {
public final Optional<Long> id; public final Optional<Long> id;
public final String name; public final String name;
public final int calories; public final int calories;
/** /**
* Constructor. * Constructor.
*/ */
public CakeLayerInfo(Long id, String name, int calories) { public CakeLayerInfo(Long id, String name, int calories) {
this.id = Optional.of(id); this.id = Optional.of(id);
this.name = name; this.name = name;
this.calories = calories; this.calories = calories;
} }
/** /**
* Constructor. * Constructor.
*/ */
public CakeLayerInfo(String name, int calories) { public CakeLayerInfo(String name, int calories) {
this.id = Optional.empty(); this.id = Optional.empty();
this.name = name; this.name = name;
this.calories = calories; this.calories = calories;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("CakeLayerInfo id=%d name=%s calories=%d", id.orElse(-1L), name, calories); return String.format("CakeLayerInfo id=%d name=%s calories=%d", id.orElse(-1L), name, calories);
} }
} }
@@ -22,7 +22,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.dto; package dto;
import java.util.Optional; import java.util.Optional;
@@ -31,31 +32,31 @@ import java.util.Optional;
*/ */
public class CakeToppingInfo { public class CakeToppingInfo {
public final Optional<Long> id; public final Optional<Long> id;
public final String name; public final String name;
public final int calories; public final int calories;
/** /**
* Constructor. * Constructor.
*/ */
public CakeToppingInfo(Long id, String name, int calories) { public CakeToppingInfo(Long id, String name, int calories) {
this.id = Optional.of(id); this.id = Optional.of(id);
this.name = name; this.name = name;
this.calories = calories; this.calories = calories;
} }
/** /**
* Constructor. * Constructor.
*/ */
public CakeToppingInfo(String name, int calories) { public CakeToppingInfo(String name, int calories) {
this.id = Optional.empty(); this.id = Optional.empty();
this.name = name; this.name = name;
this.calories = calories; this.calories = calories;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("CakeToppingInfo id=%d name=%s calories=%d", return String.format("CakeToppingInfo id=%d name=%s calories=%d",
id.orElse(-1L), name, calories); id.orElse(-1L), name, calories);
} }
} }
@@ -22,17 +22,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.entity; package entity;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.persistence.CascadeType; import jakarta.persistence.Entity;
import javax.persistence.Entity; import jakarta.persistence.GeneratedValue;
import javax.persistence.FetchType; import jakarta.persistence.Id;
import javax.persistence.GeneratedValue; import jakarta.persistence.OneToMany;
import javax.persistence.Id; import jakarta.persistence.OneToOne;
import javax.persistence.OneToMany; import jakarta.persistence.CascadeType;
import javax.persistence.OneToOne; import jakarta.persistence.FetchType;
/** /**
* Cake entity. * Cake entity.
@@ -40,50 +40,50 @@ import javax.persistence.OneToOne;
@Entity @Entity
public class Cake { public class Cake {
@Id @Id
@GeneratedValue @GeneratedValue
private Long id; private Long id;
@OneToOne(cascade = CascadeType.REMOVE) @OneToOne(cascade = CascadeType.REMOVE)
private CakeTopping topping; private CakeTopping topping;
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
private Set<CakeLayer> layers; private Set<CakeLayer> layers;
public Cake() { public Cake() {
setLayers(new HashSet<>()); setLayers(new HashSet<>());
} }
public Long getId() { public Long getId() {
return id; return id;
} }
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public CakeTopping getTopping() { public CakeTopping getTopping() {
return topping; return topping;
} }
public void setTopping(CakeTopping topping) { public void setTopping(CakeTopping topping) {
this.topping = topping; this.topping = topping;
} }
public Set<CakeLayer> getLayers() { public Set<CakeLayer> getLayers() {
return layers; return layers;
} }
public void setLayers(Set<CakeLayer> layers) { public void setLayers(Set<CakeLayer> layers) {
this.layers = layers; this.layers = layers;
} }
public void addLayer(CakeLayer layer) { public void addLayer(CakeLayer layer) {
this.layers.add(layer); this.layers.add(layer);
} }
@Override @Override
public String toString() { public String toString() {
return String.format("id=%s topping=%s layers=%s", id, topping, layers.toString()); return String.format("id=%s topping=%s layers=%s", id, topping, layers.toString());
} }
} }
@@ -22,21 +22,42 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.app; package entity;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import jakarta.persistence.*;
import lombok.*;
/** /**
* * CakeLayer entity.
* Application test
*
*/ */
class AppTest { @Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode
public class CakeLayer {
@Test @Id
void shouldExecuteApplicationWithoutException() { @GeneratedValue
assertDoesNotThrow(() -> App.main(new String[]{})); private Long id;
}
private String name;
private int calories;
@ManyToOne(cascade = CascadeType.ALL)
private Cake cake;
public CakeLayer(String name, int calories) {
this.setName(name);
this.setCalories(calories);
}
@Override
public String toString() {
return String.format("id=%s name=%s calories=%d", id, name, calories);
}
} }
@@ -0,0 +1,64 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package entity;
import jakarta.persistence.*;
import lombok.*;
/**
* CakeTopping entity.
*/
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode
public class CakeTopping {
@Id
@GeneratedValue
private Long id;
private String name;
private int calories;
@OneToOne(cascade = CascadeType.ALL)
private Cake cake;
public CakeTopping(String name, int calories) {
this.setName(name);
this.setCalories(calories);
}
@Override
public String toString() {
return String.format("id=%s name=%s calories=%d", id, name, calories);
}
}
@@ -22,19 +22,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.exception; package exception;
import org.springframework.stereotype.Component;
/** /**
* Custom exception used in cake baking. * Custom exception used in cake baking.
*/ */
@Component
public class CakeBakingException extends Exception { public class CakeBakingException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public CakeBakingException() { public CakeBakingException() {
} }
public CakeBakingException(String message) { public CakeBakingException(String message) {
super(message); super(message);
} }
} }
@@ -22,46 +22,56 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.service; package service;
import dto.CakeInfo;
import dto.CakeLayerInfo;
import dto.CakeToppingInfo;
import exception.CakeBakingException;
import org.springframework.stereotype.Service;
import com.iluwatar.layers.dto.CakeInfo;
import com.iluwatar.layers.dto.CakeLayerInfo;
import com.iluwatar.layers.dto.CakeToppingInfo;
import com.iluwatar.layers.exception.CakeBakingException;
import java.util.List; import java.util.List;
/** /**
* Service for cake baking operations. * Service for cake baking operations.
*/ */
@Service
public interface CakeBakingService { public interface CakeBakingService {
/** /**
* Bakes new cake according to parameters. * Bakes new cake according to parameters.
*/ */
void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException; void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException;
/** /**
* Get all cakes. * Get all cakes.
*/ */
List<CakeInfo> getAllCakes(); List<CakeInfo> getAllCakes();
/** /**
* Store new cake topping. * Store new cake topping.
*/ */
void saveNewTopping(CakeToppingInfo toppingInfo); void saveNewTopping(CakeToppingInfo toppingInfo);
/** /**
* Get available cake toppings. * Get available cake toppings.
*/ */
List<CakeToppingInfo> getAvailableToppings(); List<CakeToppingInfo> getAvailableToppings();
/** /**
* Add new cake layer. * Add new cake layer.
*/ */
void saveNewLayer(CakeLayerInfo layerInfo); void saveNewLayer(CakeLayerInfo layerInfo);
/**
* Get available cake layers.
*/
List<CakeLayerInfo> getAvailableLayers();
void deleteAllCakes();
void deleteAllLayers();
void deleteAllToppings();
/**
* Get available cake layers.
*/
List<CakeLayerInfo> getAvailableLayers();
} }
@@ -0,0 +1,189 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package service;
import dao.CakeDao;
import dao.CakeLayerDao;
import dao.CakeToppingDao;
import dto.CakeInfo;
import dto.CakeLayerInfo;
import dto.CakeToppingInfo;
import entity.Cake;
import entity.CakeLayer;
import entity.CakeTopping;
import exception.CakeBakingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Implementation of CakeBakingService.
*/
@Service
@Transactional
public class CakeBakingServiceImpl implements CakeBakingService {
private final CakeDao cakeDao;
private final CakeLayerDao cakeLayerDao;
private final CakeToppingDao cakeToppingDao;
@Autowired
public CakeBakingServiceImpl(CakeDao cakeDao, CakeLayerDao cakeLayerDao, CakeToppingDao cakeToppingDao) {
this.cakeDao = cakeDao;
this.cakeLayerDao = cakeLayerDao;
this.cakeToppingDao = cakeToppingDao;
}
@Override
public void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException {
var allToppings = getAvailableToppingEntities();
var matchingToppings =
allToppings.stream().filter(t -> t.getName().equals(cakeInfo.cakeToppingInfo.name))
.toList();
if (matchingToppings.isEmpty()) {
throw new CakeBakingException(String.format("Topping %s is not available",
cakeInfo.cakeToppingInfo.name));
}
var allLayers = getAvailableLayerEntities();
Set<CakeLayer> foundLayers = new HashSet<>();
for (var info : cakeInfo.cakeLayerInfos) {
var found = allLayers.stream().filter(layer -> layer.getName().equals(info.name)).findFirst();
if (found.isEmpty()) {
throw new CakeBakingException(String.format("Layer %s is not available", info.name));
} else {
foundLayers.add(found.get());
}
}
var topping = cakeToppingDao.findById(matchingToppings.iterator().next().getId());
if (topping.isPresent()) {
var cake = new Cake();
cake.setTopping(topping.get());
cake.setLayers(foundLayers);
cakeDao.save(cake);
topping.get().setCake(cake);
cakeToppingDao.save(topping.get());
Set<CakeLayer> foundLayersToUpdate = new HashSet<>(foundLayers); // copy set to avoid a ConcurrentModificationException
for (var layer : foundLayersToUpdate) {
layer.setCake(cake);
cakeLayerDao.save(layer);
}
} else {
throw new CakeBakingException(String.format("Topping %s is not available",
cakeInfo.cakeToppingInfo.name));
}
}
@Override
public void saveNewTopping(CakeToppingInfo toppingInfo) {
cakeToppingDao.save(new CakeTopping(toppingInfo.name, toppingInfo.calories));
}
@Override
public void saveNewLayer(CakeLayerInfo layerInfo) {
cakeLayerDao.save(new CakeLayer(layerInfo.name, layerInfo.calories));
}
private List<CakeTopping> getAvailableToppingEntities() {
List<CakeTopping> result = new ArrayList<>();
for (CakeTopping topping : cakeToppingDao.findAll()) {
if (topping.getCake() == null) {
result.add(topping);
}
}
return result;
}
@Override
public List<CakeToppingInfo> getAvailableToppings() {
List<CakeToppingInfo> result = new ArrayList<>();
for (CakeTopping next : cakeToppingDao.findAll()) {
if (next.getCake() == null) {
result.add(new CakeToppingInfo(next.getId(), next.getName(), next.getCalories()));
}
}
return result;
}
private List<CakeLayer> getAvailableLayerEntities() {
List<CakeLayer> result = new ArrayList<>();
for (CakeLayer next : cakeLayerDao.findAll()) {
if (next.getCake() == null) {
result.add(next);
}
}
return result;
}
@Override
public List<CakeLayerInfo> getAvailableLayers() {
List<CakeLayerInfo> result = new ArrayList<>();
for (CakeLayer next : cakeLayerDao.findAll()) {
if (next.getCake() == null) {
result.add(new CakeLayerInfo(next.getId(), next.getName(), next.getCalories()));
}
}
return result;
}
@Override
public void deleteAllCakes() {
cakeDao.deleteAll();
}
@Override
public void deleteAllLayers() {
cakeLayerDao.deleteAll();
}
@Override
public void deleteAllToppings() {
cakeToppingDao.deleteAll();
}
@Override
public List<CakeInfo> getAllCakes() {
List<CakeInfo> result = new ArrayList<>();
for (Cake cake : cakeDao.findAll()) {
var cakeToppingInfo =
new CakeToppingInfo(cake.getTopping().getId(), cake.getTopping().getName(), cake
.getTopping().getCalories());
List<CakeLayerInfo> cakeLayerInfos = new ArrayList<>();
for (var layer : cake.getLayers()) {
cakeLayerInfos.add(new CakeLayerInfo(layer.getId(), layer.getName(), layer.getCalories()));
}
var cakeInfo = new CakeInfo(cake.getId(), cakeToppingInfo, cakeLayerInfos);
result.add(cakeInfo);
}
return result;
}
}
@@ -22,24 +22,26 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.view; package view;
import com.iluwatar.layers.service.CakeBakingService; import org.slf4j.Logger;
import lombok.extern.slf4j.Slf4j; import org.slf4j.LoggerFactory;
import service.CakeBakingService;
/** /**
* View implementation for displaying cakes. * View implementation for displaying cakes.
*/ */
@Slf4j
public class CakeViewImpl implements View { public class CakeViewImpl implements View {
private final CakeBakingService cakeBakingService; private final CakeBakingService cakeBakingService;
public CakeViewImpl(CakeBakingService cakeBakingService) { private static final Logger LOGGER = LoggerFactory.getLogger(CakeViewImpl.class);
this.cakeBakingService = cakeBakingService;
}
public void render() { public CakeViewImpl(CakeBakingService cakeBakingService) {
cakeBakingService.getAllCakes().forEach(cake -> LOGGER.info(cake.toString())); this.cakeBakingService = cakeBakingService;
} }
public void render() {
cakeBakingService.getAllCakes().forEach(cake -> LOGGER.info(cake.toString()));
}
} }
@@ -22,13 +22,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.layers.view; package view;
/** /**
* View interface. * View interface.
*/ */
public interface View { public interface View {
void render(); void render();
} }
@@ -24,30 +24,30 @@
--> -->
<configuration> <configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>layers.log</file> <file>layers.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>layers-%d.log</fileNamePattern> <fileNamePattern>layers-%d.log</fileNamePattern>
<maxHistory>5</maxHistory> <maxHistory>5</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
<pattern>%-5p [%d{ISO8601,UTC}] %c: %m%n</pattern> <pattern>%-5p [%d{ISO8601,UTC}] %c: %m%n</pattern>
</encoder> </encoder>
</appender> </appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>%-5p [%d{ISO8601,UTC}] %c: %m%n</pattern> <pattern>%-5p [%d{ISO8601,UTC}] %c: %m%n</pattern>
</encoder> </encoder>
</appender> </appender>
<logger name="com.iluwatar" additivity="false"> <logger name="com.iluwatar" additivity="false">
<level value="DEBUG" /> <level value="DEBUG" />
<appender-ref ref="FILE" /> <appender-ref ref="FILE" />
</logger> </logger>
<logger name="org.hibernate" additivity="false"> <logger name="org.hibernate" additivity="false">
<level value="ERROR" /> <level value="ERROR" />
<appender-ref ref="STDOUT" /> <appender-ref ref="STDOUT" />
</logger> </logger>
<root level="WARN"> <root level="WARN">
<appender-ref ref="STDOUT" /> <appender-ref ref="STDOUT" />
</root> </root>
</configuration> </configuration>
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="jpaData" />
</persistence>
@@ -0,0 +1,12 @@
spring.main.web-application-type=none
#datasource settings
spring.datasource.url=jdbc:h2:~/databases/cake
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa
#data settings
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:security="http://www.springframework.org/schema/security" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<jpa:repositories base-package="com.iluwatar" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:~/databases/cake" />
<property name="username" value="sa" />
<property name="password" value="sa" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.iluwatar" />
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
</property>
<property name="jpaProperties">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<entry key="hibernate.hbm2ddl.auto" value="create-drop" />
<entry key="hibernate.show_sql" value="false" />
</map>
</property>
</bean>
<context:component-scan base-package="com.iluwatar.layers.dao">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
</context:component-scan>
</beans>
@@ -0,0 +1,24 @@
package com.iluwatar.layers.app;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@SpringBootTest(classes = LayersApp.class)
class LayersAppTests {
private final ApplicationContext applicationContext;
@Autowired
LayersAppTests(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Test
void contextLoads() {
assertNotNull(applicationContext);
}
}
@@ -31,6 +31,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import entity.Cake;
import entity.CakeLayer;
import entity.CakeTopping;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
@@ -27,6 +27,7 @@ package com.iluwatar.layers.exception;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import exception.CakeBakingException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
@@ -34,8 +35,10 @@ import org.junit.jupiter.api.Test;
* *
* @author Jeroen Meulemeester * @author Jeroen Meulemeester
*/ */
class CakeBakingExceptionTest { class CakeBakingExceptionTest {
@Test @Test
void testConstructor() { void testConstructor() {
final var exception = new CakeBakingException(); final var exception = new CakeBakingException();
@@ -30,33 +30,56 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import com.iluwatar.layers.dto.CakeInfo; import com.iluwatar.layers.app.LayersApp;
import com.iluwatar.layers.dto.CakeLayerInfo; import dto.CakeInfo;
import com.iluwatar.layers.dto.CakeToppingInfo; import dto.CakeLayerInfo;
import com.iluwatar.layers.exception.CakeBakingException; import dto.CakeToppingInfo;
import exception.CakeBakingException;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import service.CakeBakingServiceImpl;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* Date: 12/15/15 - 9:55 PM * Date: 12/15/15 - 9:55 PM
* *
* @author Jeroen Meulemeester * @author Jeroen Meulemeester
*/ */
@SpringBootTest(classes = LayersApp.class)
class CakeBakingServiceImplTest { class CakeBakingServiceImplTest {
@Test private final CakeBakingServiceImpl cakeBakingService;
void testLayers() {
final var service = new CakeBakingServiceImpl();
final var initialLayers = service.getAvailableLayers(); @Autowired
CakeBakingServiceImplTest(CakeBakingServiceImpl cakeBakingService) {
this.cakeBakingService = cakeBakingService;
}
@BeforeEach
void setUp() {
cakeBakingService.deleteAllCakes();
cakeBakingService.deleteAllLayers();
cakeBakingService.deleteAllToppings();
}
@Test
void testLayers() {
final var initialLayers = cakeBakingService.getAvailableLayers();
assertNotNull(initialLayers); assertNotNull(initialLayers);
assertTrue(initialLayers.isEmpty()); assertTrue(initialLayers.isEmpty());
service.saveNewLayer(new CakeLayerInfo("Layer1", 1000)); cakeBakingService.saveNewLayer(new CakeLayerInfo("Layer1", 1000));
service.saveNewLayer(new CakeLayerInfo("Layer2", 2000)); cakeBakingService.saveNewLayer(new CakeLayerInfo("Layer2", 2000));
final var availableLayers = service.getAvailableLayers(); final var availableLayers = cakeBakingService.getAvailableLayers();
assertNotNull(availableLayers); assertNotNull(availableLayers);
assertEquals(2, availableLayers.size()); assertEquals(2, availableLayers.size());
for (final var layer : availableLayers) { for (final var layer : availableLayers) {
@@ -70,16 +93,14 @@ class CakeBakingServiceImplTest {
@Test @Test
void testToppings() { void testToppings() {
final var service = new CakeBakingServiceImpl(); final var initialToppings = cakeBakingService.getAvailableToppings();
final var initialToppings = service.getAvailableToppings();
assertNotNull(initialToppings); assertNotNull(initialToppings);
assertTrue(initialToppings.isEmpty()); assertTrue(initialToppings.isEmpty());
service.saveNewTopping(new CakeToppingInfo("Topping1", 1000)); cakeBakingService.saveNewTopping(new CakeToppingInfo("Topping1", 1000));
service.saveNewTopping(new CakeToppingInfo("Topping2", 2000)); cakeBakingService.saveNewTopping(new CakeToppingInfo("Topping2", 2000));
final var availableToppings = service.getAvailableToppings(); final var availableToppings = cakeBakingService.getAvailableToppings();
assertNotNull(availableToppings); assertNotNull(availableToppings);
assertEquals(2, availableToppings.size()); assertEquals(2, availableToppings.size());
for (final var topping : availableToppings) { for (final var topping : availableToppings) {
@@ -93,28 +114,27 @@ class CakeBakingServiceImplTest {
@Test @Test
void testBakeCakes() throws CakeBakingException { void testBakeCakes() throws CakeBakingException {
final var service = new CakeBakingServiceImpl();
final var initialCakes = service.getAllCakes(); final var initialCakes = cakeBakingService.getAllCakes();
assertNotNull(initialCakes); assertNotNull(initialCakes);
assertTrue(initialCakes.isEmpty()); assertTrue(initialCakes.isEmpty());
final var topping1 = new CakeToppingInfo("Topping1", 1000); final var topping1 = new CakeToppingInfo("Topping1", 1000);
final var topping2 = new CakeToppingInfo("Topping2", 2000); final var topping2 = new CakeToppingInfo("Topping2", 2000);
service.saveNewTopping(topping1); cakeBakingService.saveNewTopping(topping1);
service.saveNewTopping(topping2); cakeBakingService.saveNewTopping(topping2);
final var layer1 = new CakeLayerInfo("Layer1", 1000); final var layer1 = new CakeLayerInfo("Layer1", 1000);
final var layer2 = new CakeLayerInfo("Layer2", 2000); final var layer2 = new CakeLayerInfo("Layer2", 2000);
final var layer3 = new CakeLayerInfo("Layer3", 2000); final var layer3 = new CakeLayerInfo("Layer3", 2000);
service.saveNewLayer(layer1); cakeBakingService.saveNewLayer(layer1);
service.saveNewLayer(layer2); cakeBakingService.saveNewLayer(layer2);
service.saveNewLayer(layer3); cakeBakingService.saveNewLayer(layer3);
service.bakeNewCake(new CakeInfo(topping1, List.of(layer1, layer2))); cakeBakingService.bakeNewCake(new CakeInfo(topping1, List.of(layer1, layer2)));
service.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer3))); cakeBakingService.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer3)));
final var allCakes = service.getAllCakes(); final var allCakes = cakeBakingService.getAllCakes();
assertNotNull(allCakes); assertNotNull(allCakes);
assertEquals(2, allCakes.size()); assertEquals(2, allCakes.size());
for (final var cakeInfo : allCakes) { for (final var cakeInfo : allCakes) {
@@ -130,61 +150,49 @@ class CakeBakingServiceImplTest {
@Test @Test
void testBakeCakeMissingTopping() { void testBakeCakeMissingTopping() {
final var service = new CakeBakingServiceImpl();
final var layer1 = new CakeLayerInfo("Layer1", 1000); final var layer1 = new CakeLayerInfo("Layer1", 1000);
final var layer2 = new CakeLayerInfo("Layer2", 2000); final var layer2 = new CakeLayerInfo("Layer2", 2000);
service.saveNewLayer(layer1); cakeBakingService.saveNewLayer(layer1);
service.saveNewLayer(layer2); cakeBakingService.saveNewLayer(layer2);
final var missingTopping = new CakeToppingInfo("Topping1", 1000); final var missingTopping = new CakeToppingInfo("Topping1", 1000);
assertThrows(CakeBakingException.class, () -> { assertThrows(CakeBakingException.class, () -> cakeBakingService.bakeNewCake(new CakeInfo(missingTopping, List.of(layer1, layer2))));
service.bakeNewCake(new CakeInfo(missingTopping, List.of(layer1, layer2)));
});
} }
@Test @Test
void testBakeCakeMissingLayer() { void testBakeCakeMissingLayer() {
final var service = new CakeBakingServiceImpl(); final var initialCakes = cakeBakingService.getAllCakes();
final var initialCakes = service.getAllCakes();
assertNotNull(initialCakes); assertNotNull(initialCakes);
assertTrue(initialCakes.isEmpty()); assertTrue(initialCakes.isEmpty());
final var topping1 = new CakeToppingInfo("Topping1", 1000); final var topping1 = new CakeToppingInfo("Topping1", 1000);
service.saveNewTopping(topping1); cakeBakingService.saveNewTopping(topping1);
final var layer1 = new CakeLayerInfo("Layer1", 1000); final var layer1 = new CakeLayerInfo("Layer1", 1000);
service.saveNewLayer(layer1); cakeBakingService.saveNewLayer(layer1);
final var missingLayer = new CakeLayerInfo("Layer2", 2000); final var missingLayer = new CakeLayerInfo("Layer2", 2000);
assertThrows(CakeBakingException.class, () -> { assertThrows(CakeBakingException.class, () -> cakeBakingService.bakeNewCake(new CakeInfo(topping1, List.of(layer1, missingLayer))));
service.bakeNewCake(new CakeInfo(topping1, List.of(layer1, missingLayer)));
});
} }
@Test @Test
void testBakeCakesUsedLayer() throws CakeBakingException { void testBakeCakesUsedLayer() throws CakeBakingException {
final var service = new CakeBakingServiceImpl(); final var initialCakes = cakeBakingService.getAllCakes();
final var initialCakes = service.getAllCakes();
assertNotNull(initialCakes); assertNotNull(initialCakes);
assertTrue(initialCakes.isEmpty()); assertTrue(initialCakes.isEmpty());
final var topping1 = new CakeToppingInfo("Topping1", 1000); final var topping1 = new CakeToppingInfo("Topping1", 1000);
final var topping2 = new CakeToppingInfo("Topping2", 2000); final var topping2 = new CakeToppingInfo("Topping2", 2000);
service.saveNewTopping(topping1); cakeBakingService.saveNewTopping(topping1);
service.saveNewTopping(topping2); cakeBakingService.saveNewTopping(topping2);
final var layer1 = new CakeLayerInfo("Layer1", 1000); final var layer1 = new CakeLayerInfo("Layer1", 1000);
final var layer2 = new CakeLayerInfo("Layer2", 2000); final var layer2 = new CakeLayerInfo("Layer2", 2000);
service.saveNewLayer(layer1); cakeBakingService.saveNewLayer(layer1);
service.saveNewLayer(layer2); cakeBakingService.saveNewLayer(layer2);
service.bakeNewCake(new CakeInfo(topping1, List.of(layer1, layer2))); cakeBakingService.bakeNewCake(new CakeInfo(topping1, List.of(layer1, layer2)));
assertThrows(CakeBakingException.class, () -> { assertThrows(CakeBakingException.class, () -> cakeBakingService.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer2))));
service.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer2)));
});
} }
} }
@@ -31,16 +31,17 @@ import static org.mockito.Mockito.when;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.AppenderBase;
import com.iluwatar.layers.dto.CakeInfo; import dto.CakeInfo;
import com.iluwatar.layers.dto.CakeLayerInfo; import dto.CakeLayerInfo;
import com.iluwatar.layers.dto.CakeToppingInfo; import dto.CakeToppingInfo;
import com.iluwatar.layers.service.CakeBakingService; import service.CakeBakingService;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import view.CakeViewImpl;
/** /**
* Date: 12/15/15 - 10:04 PM * Date: 12/15/15 - 10:04 PM
@@ -68,9 +69,9 @@ class CakeViewImplTest {
void testRender() { void testRender() {
final var layers = List.of( final var layers = List.of(
new CakeLayerInfo("layer1", 1000), new CakeLayerInfo("layer1", 1000),
new CakeLayerInfo("layer2", 2000), new CakeLayerInfo("layer2", 2000),
new CakeLayerInfo("layer3", 3000)); new CakeLayerInfo("layer3", 3000));
final var cake = new CakeInfo(new CakeToppingInfo("topping", 1000), layers); final var cake = new CakeInfo(new CakeToppingInfo("topping", 1000), layers);
final var cakes = List.of(cake); final var cakes = List.of(cake);
+1 -1
View File
@@ -333,7 +333,7 @@
<configuration> <configuration>
<configLocation>google_checks.xml</configLocation> <configLocation>google_checks.xml</configLocation>
<suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation> <suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation>
<encoding>UTF-8</encoding>
<failOnViolation>true</failOnViolation> <failOnViolation>true</failOnViolation>
<violationSeverity>warning</violationSeverity> <violationSeverity>warning</violationSeverity>
<includeTestSourceDirectory>false</includeTestSourceDirectory> <includeTestSourceDirectory>false</includeTestSourceDirectory>