diff --git a/README.md b/README.md index a499e75bb8ec82e6bcb087db5bd6e4fe17024217..2f6c5f4725843e54fd7037159795a446375a7218 100644 --- a/README.md +++ b/README.md @@ -1 +1,82 @@ -# 11-futures-cli \ No newline at end of file +_This is an assignment to the class [Programmieren 3](https://hsro-inf-prg3.github.io) at the [University of Applied Sciences Rosenheim](http://www.fh-rosenheim.de)._ + +# Assignment 11: Futures - CLI variant + +This assignment covers the more advanced multithreading topic _futures_ and _future chaining_. +Futures are a feature of Java 8 and can be compared to the concept of _promises_ in JavaScript. + +The internet contains lots of good articles about `Future<>` and `CompletableFuture<>` in Java. +For example [this one](http://www.deadcoderising.com/java8-writing-asynchronous-code-with-completablefuture/) covers everything you need to know for this assignment. + +_This is the CLI variant of the assignment. There's also an [Android variant](https://github.com/hsro-inf-prg3/11-futures-android) which covers the same concepts. **You don't have to implement both!** If you want to switch between them you should be able to copy most of the code you already wrote (except a few platform adoptions)._ + +## Setup + +1. Create a fork of this repository (button in the right upper corner) +1. Clone the project (get the link by clicking the green _Clone or download button_) +1. Import the project to your **IntelliJ** + +_Remark: the given unit tests won't succeed until you have completed the first part of this assignment as they require the `CompletableFuture<>` Call Adapter registered in Retrofit!_ + +## Retrofit Call Adapter + +To be able to use the given `OpenMensaAPI` interface with Retrofit you have to do some extra work. +Retrofit is able to return `CompletableFuture<>` but you have to register a [Call Adapter](https://github.com/square/retrofit/wiki/Call-Adapters) to enable this feature. + +To accomplish this you have to include another dependency in the `build.gradle`-file and register the adapter within the Retrofit builder (`.addCallAdapterFactory(...)`). + +To validate that the Call Adapter is registered correctly run the given unit tests. +They're also a good starting point to get an idea how to use the API with `Future<>`s instead of the Retrofit specific `Call<>` objects. + +## Retrieving all canteens + +The given CLI application has a menu which asks the user which action he wants to perform. +The following actions are available: + +* Show canteens - retrieves all canteens and prints them (including the id) to the STDOUT +* Set canteen - reads an integer as canteen id +* Set date - reads a date string and updates the currently selected date +* Show meals - retrieves all meals of the currently selected canteen and date and prints them to the STDOUT + +The first step is to complete the `printCanteens()` method. +The following flow chart shows how to proceed : + + + +### Retrieve the first page of canteens + +Use the method `getCanteens()` of the OpenMensaAPI to retrieve the fist page of canteens (without index). +The method returns an object of `Response<List<Canteen>>`. +That might be a little bit confusing but the OpenMensaAPI does not expose a dedicated pagination endpoint to retrieve the total count of items or the total count of pages but exposes this information in the response headers (`X-Total-Pages`, `X-Total-Count`, ...). +To be able extract this information you need the `Response<>` wrapper because the wrapper includes a reference to the headers. + +### Extract the pagination information + +There's a given utility class `PageInfo` which extracts the pagination information from a generic `Response<>` object. To create a `PageInfo` instance use the static factory method `PageInfo.extractFromResponse(...)`. + +### Retrieve the remaining pages + +When you have the pagination information extracted you can retrieve the remaining pages with the method `getCanteens(int pageNumber)` of the OpenMensaAPI. +As always there's not **one** solution but different ways to accomplish this! +But no matter which approach you're chosing you have to wait for all responses to be retrieved to display a complete list of canteens before you leave the method because otherwise the main menu is printed in the middle of the list of canteens. + +## Retrieve the meals of a day + +The second (and last) step is to complete the method `printMeals()`. +This method should retrieve the meals of specific day served at a specified canteen. + +The following flow chart shows how to proceed: + + + +### Retrieve the canteen state + +At first you have to validate that there's a canteen selected (i.e. the `currentCanteenId` may not be less or equal 0!). +Afterwards you should fetch the state of the canteen at the given date (default date is today, `dateFormat.format(currentDate.getTime())` formats the currently selected date as required by the API). +The required method to fetch a state is already present in the OpenMensaAPI. + +### Retrieve the meals + +If you retrieved the state of the canteen at it is open at the specified date you have to retrieve the meals for the same params. +Keep in mind to block the main thread to display the retrieved meals after you retrieved them. +If the canteen is not open at the specified date a meaningful message should be printed to inform the user, that the date has to be changed. \ No newline at end of file diff --git a/assets/images/CanteenRetrievalFlow.svg b/assets/images/CanteenRetrievalFlow.svg new file mode 100644 index 0000000000000000000000000000000000000000..fb4f0d47e7f00f75bb789238d5fcb030bf267586 --- /dev/null +++ b/assets/images/CanteenRetrievalFlow.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="321px" preserveAspectRatio="none" style="width:211px;height:321px;" version="1.1" viewBox="0 0 211 321" width="211px" zoomAndPan="magnify"><defs><filter height="300%" id="fkj6udqs5892f" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="103" cy="18" fill="#000000" filter="url(#fkj6udqs5892f)" rx="10" ry="10" style="stroke: none; stroke-width: 1.0;"/><rect fill="#FEFECE" filter="url(#fkj6udqs5892f)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="194" x="6" y="68"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="174" x="16" y="89.1387">Retrieve page without index</text><rect fill="#FEFECE" filter="url(#fkj6udqs5892f)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="123" x="41.5" y="142"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="103" x="51.5" y="163.1387">Extract PageInfo</text><rect fill="#FEFECE" filter="url(#fkj6udqs5892f)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="180" x="13" y="216"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="160" x="23" y="237.1387">Retrieve remaining pages</text><ellipse cx="103" cy="300" fill="none" filter="url(#fkj6udqs5892f)" rx="10" ry="10" style="stroke: #000000; stroke-width: 1.0;"/><ellipse cx="103.5" cy="300.5" fill="#000000" rx="6" ry="6" style="stroke: none; stroke-width: 1.0;"/><!--link start to Retrieve page without index--><path d="M103,28.1759 C103,37.3186 103,51.0626 103,62.7436 " fill="none" id="start-Retrieve page without index" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="103,67.9414,107,58.9414,103,62.9414,99,58.9414,103,67.9414" style="stroke: #A80036; stroke-width: 1.0;"/><!--link Retrieve page without index to Extract PageInfo--><path d="M103,102.1631 C103,112.4368 103,125.6075 103,136.6831 " fill="none" id="Retrieve page without index-Extract PageInfo" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="103,141.93,107,132.93,103,136.93,99,132.93,103,141.93" style="stroke: #A80036; stroke-width: 1.0;"/><!--link Extract PageInfo to Retrieve remaining pages--><path d="M103,176.1631 C103,186.4368 103,199.6075 103,210.6831 " fill="none" id="Extract PageInfo-Retrieve remaining pages" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="103,215.93,107,206.93,103,210.93,99,206.93,103,215.93" style="stroke: #A80036; stroke-width: 1.0;"/><!--link Retrieve remaining pages to end--><path d="M103,250.2542 C103,260.9518 103,274.5657 103,284.8464 " fill="none" id="Retrieve remaining pages-end" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="103,289.8855,107,280.8855,103,284.8855,99,280.8855,103,289.8855" style="stroke: #A80036; stroke-width: 1.0;"/><!-- +@startuml CanteenRetrievalFlow + +(*) - -> "Retrieve page without index" +- ->"Extract PageInfo" +- ->"Retrieve remaining pages" +- ->(*) + +@enduml + +PlantUML version 1.2017.20(Mon Dec 11 17:57:05 CET 2017) +(GPL source distribution) +Java Runtime: OpenJDK Runtime Environment +JVM: OpenJDK 64-Bit Server VM +Java Version: 1.8.0_144-b01 +Operating System: Linux +OS Version: 4.14.6-1-ARCH +Default Encoding: UTF-8 +Language: en +Country: US +--></g></svg> \ No newline at end of file diff --git a/assets/images/MealsRetrievalFlow.svg b/assets/images/MealsRetrievalFlow.svg new file mode 100644 index 0000000000000000000000000000000000000000..1a81f06a9fe9b6ca5f1d9d179dc5062fc4814959 --- /dev/null +++ b/assets/images/MealsRetrievalFlow.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="328px" preserveAspectRatio="none" style="width:275px;height:328px;" version="1.1" viewBox="0 0 275 328" width="275px" zoomAndPan="magnify"><defs><filter height="300%" id="f15069ewvtnr2d" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="131" cy="18" fill="#000000" filter="url(#f15069ewvtnr2d)" rx="10" ry="10" style="stroke: none; stroke-width: 1.0;"/><rect fill="#FEFECE" filter="url(#f15069ewvtnr2d)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="164" x="49" y="69"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="144" x="59" y="90.1387">Retrieve canteen state</text><polygon fill="#FEFECE" filter="url(#f15069ewvtnr2d)" points="131,144,143,156,131,168,119,156,131,144" style="stroke: #A80036; stroke-width: 1.5;"/><rect fill="#FEFECE" filter="url(#f15069ewvtnr2d)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="112" x="6" y="222"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="92" x="16" y="243.1387">Retrieve meals</text><ellipse cx="131" cy="307" fill="none" filter="url(#f15069ewvtnr2d)" rx="10" ry="10" style="stroke: #000000; stroke-width: 1.0;"/><ellipse cx="131.5" cy="307.5" fill="#000000" rx="6" ry="6" style="stroke: none; stroke-width: 1.0;"/><rect fill="#FEFECE" filter="url(#f15069ewvtnr2d)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="126" x="138" y="222"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="106" x="148" y="243.1387">Return empty list</text><!--link start to Retrieve canteen state--><path d="M131,28.0336 C131,37.4082 131,51.7285 131,63.7898 " fill="none" id="start-Retrieve canteen state" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="131,68.7995,135,59.7995,131,63.7995,127,59.7995,131,68.7995" style="stroke: #A80036; stroke-width: 1.0;"/><!--link Retrieve canteen state to #5--><path d="M131,103.3034 C131,114.1588 131,128.0963 131,138.9108 " fill="none" id="Retrieve canteen state-#5" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="131,143.9417,135,134.9417,131,138.9417,127,134.9417,131,143.9417" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="97" x="13.9937" y="134.8691">canteen.isOpen()</text><!--link #5 to Retrieve meals--><path d="M125.3534,162.7922 C115.4789,174.6703 94.6908,199.6763 79.6138,217.8124 " fill="none" id="#5-Retrieve meals" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="76.2982,221.8008,85.1276,217.437,79.4946,217.9559,78.9757,212.3229,76.2982,221.8008" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="23" x="104" y="199.2104">true</text><!--link Retrieve meals to end--><path d="M79.4116,256.1593 C92.212,268.7741 109.2112,285.5269 120.1307,296.2882 " fill="none" id="Retrieve meals-end" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="123.8595,299.963,120.257,290.7966,120.2983,296.4533,114.6416,296.4946,123.8595,299.963" style="stroke: #A80036; stroke-width: 1.0;"/><!--link #5 to Return empty list--><path d="M136.4928,162.5129 C146.4408,174.3084 167.8379,199.6792 183.2647,217.971 " fill="none" id="#5-Return empty list" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="186.655,221.9909,183.9107,212.5321,183.4316,218.1686,177.7951,217.6896,186.655,221.9909" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="28" x="169" y="199.2104">false</text><!--link Return empty list to end--><path d="M183.336,256.1593 C170.3502,268.7741 153.1046,285.5269 142.0269,296.2882 " fill="none" id="Return empty list-end" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="138.2439,299.963,147.4865,296.561,141.8303,296.4791,141.9122,290.8228,138.2439,299.963" style="stroke: #A80036; stroke-width: 1.0;"/><!-- +@startuml MealsRetrievalFlow + +(*) - -> "Retrieve canteen state" +if "canteen.isOpen()" then + - ->[true] "Retrieve meals" + - -> (*) +else +- ->[false] "Return empty list" +- -> (*) +endif +@enduml + +PlantUML version 1.2017.20(Mon Dec 11 17:57:05 CET 2017) +(GPL source distribution) +Java Runtime: OpenJDK Runtime Environment +JVM: OpenJDK 64-Bit Server VM +Java Version: 1.8.0_144-b01 +Operating System: Linux +OS Version: 4.14.6-1-ARCH +Default Encoding: UTF-8 +Language: en +Country: US +--></g></svg> \ No newline at end of file diff --git a/assets/uml/CanteenRetrievalFlow.plantuml b/assets/uml/CanteenRetrievalFlow.plantuml new file mode 100644 index 0000000000000000000000000000000000000000..ab0fef853e4c688a63a44607b64d5ad12a6382db --- /dev/null +++ b/assets/uml/CanteenRetrievalFlow.plantuml @@ -0,0 +1,8 @@ +@startuml CanteenRetrievalFlow + +(*) --> "Retrieve page without index" +-->"Extract PageInfo" +-->"Retrieve remaining pages" +-->(*) + +@enduml \ No newline at end of file diff --git a/assets/uml/MealsRetrievalFlow.plantuml b/assets/uml/MealsRetrievalFlow.plantuml new file mode 100644 index 0000000000000000000000000000000000000000..04a8304a79d9eda74cf2466cb480c8cc8326698e --- /dev/null +++ b/assets/uml/MealsRetrievalFlow.plantuml @@ -0,0 +1,11 @@ +@startuml MealsRetrievalFlow + +(*) --> "Retrieve canteen state" +if "canteen.isOpen()" then + -->[true] "Retrieve meals" + --> (*) +else +-->[false] "Return empty list" +--> (*) +endif +@enduml \ No newline at end of file diff --git a/src/main/java/de/fhro/inf/prg3/a11/App.java b/src/main/java/de/fhro/inf/prg3/a11/App.java index aa61eb10ca110ff8f3b080cbcad8bfa91da54462..59ef3edb02ebe31260afed03b6a065d50778134d 100644 --- a/src/main/java/de/fhro/inf/prg3/a11/App.java +++ b/src/main/java/de/fhro/inf/prg3/a11/App.java @@ -24,7 +24,7 @@ public class App { private static final Calendar currentDate = Calendar.getInstance(); private static int currentCanteenId = -1; - public static void main(String[] args) throws ParseException { + public static void main(String[] args) { MenuSelection selection; /* loop while true to get back to the menu every time an action was performed */ do {