diff --git a/chain/README.md b/chain/README.md index f5f6c9de9..1d7eb5b73 100644 --- a/chain/README.md +++ b/chain/README.md @@ -16,7 +16,131 @@ Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. -![alt text](./etc/chain_1.png "Chain of Responsibility") +## Explanation + +Real world example + +> The Orc King gives loud orders to his army. The closest one to react is the commander, then officer and then soldier. The commander, officer and soldier here form a chain of responsibility. + +In plain words + +> It helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler. + +Wikipedia says + +> In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. + +**Programmatic Example** + +Translating our example with orcs from above. First we have the request class + +``` +public class Request { + + private final RequestType requestType; + private final String requestDescription; + private boolean handled; + + public Request(final RequestType requestType, final String requestDescription) { + this.requestType = Objects.requireNonNull(requestType); + this.requestDescription = Objects.requireNonNull(requestDescription); + } + + public String getRequestDescription() { return requestDescription; } + + public RequestType getRequestType() { return requestType; } + + public void markHandled() { this.handled = true; } + + public boolean isHandled() { return this.handled; } + + @Override + public String toString() { return getRequestDescription(); } +} + +public enum RequestType { + DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX +} +``` + +Then the request handler hierarchy + +``` +public abstract class RequestHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class); + private RequestHandler next; + + public RequestHandler(RequestHandler next) { + this.next = next; + } + + public void handleRequest(Request req) { + if (next != null) { + next.handleRequest(req); + } + } + + protected void printHandling(Request req) { + LOGGER.info("{} handling request \"{}\"", this, req); + } + + @Override + public abstract String toString(); +} + +public class OrcCommander extends RequestHandler { + public OrcCommander(RequestHandler handler) { + super(handler); + } + + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } + + @Override + public String toString() { + return "Orc commander"; + } +} + +// OrcOfficer and OrcSoldier are defined similarly as OrcCommander + +``` + +Then we have the Orc King who gives the orders and forms the chain + +``` +public class OrcKing { + RequestHandler chain; + + public OrcKing() { + buildChain(); + } + + private void buildChain() { + chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null))); + } + + public void makeRequest(Request req) { + chain.handleRequest(req); + } +} +``` + +Then it is used as follows + +``` +OrcKing king = new OrcKing(); +king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); // Orc commander handling request "defend castle" +king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); // Orc officer handling request "torture prisoner" +king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax" +``` ## Applicability Use Chain of Responsibility when diff --git a/chain/etc/chain.png b/chain/etc/chain.png deleted file mode 100644 index 7316ff3d0..000000000 Binary files a/chain/etc/chain.png and /dev/null differ diff --git a/chain/etc/chain.ucls b/chain/etc/chain.ucls deleted file mode 100644 index 4f82b40c7..000000000 --- a/chain/etc/chain.ucls +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/chain/etc/chain.urm.puml b/chain/etc/chain.urm.puml deleted file mode 100644 index 4a2f6a188..000000000 --- a/chain/etc/chain.urm.puml +++ /dev/null @@ -1,61 +0,0 @@ -@startuml -package com.iluwatar.chain { - class App { - + App() - + main(args : String[]) {static} - } - class OrcCommander { - + OrcCommander(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String - } - class OrcKing { - ~ chain : RequestHandler - + OrcKing() - - buildChain() - + makeRequest(req : Request) - } - class OrcOfficer { - + OrcOfficer(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String - } - class OrcSoldier { - + OrcSoldier(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String - } - class Request { - - handled : boolean - - requestDescription : String - - requestType : RequestType - + Request(requestType : RequestType, requestDescription : String) - + getRequestDescription() : String - + getRequestType() : RequestType - + isHandled() : boolean - + markHandled() - + toString() : String - } - abstract class RequestHandler { - - LOGGER : Logger {static} - - next : RequestHandler - + RequestHandler(next : RequestHandler) - + handleRequest(req : Request) - # printHandling(req : Request) - + toString() : String {abstract} - } - enum RequestType { - + COLLECT_TAX {static} - + DEFEND_CASTLE {static} - + TORTURE_PRISONER {static} - + valueOf(name : String) : RequestType {static} - + values() : RequestType[] {static} - } -} -RequestHandler --> "-next" RequestHandler -Request --> "-requestType" RequestType -OrcKing --> "-chain" RequestHandler -OrcCommander --|> RequestHandler -OrcOfficer --|> RequestHandler -OrcSoldier --|> RequestHandler -@enduml \ No newline at end of file diff --git a/chain/etc/chain_1.png b/chain/etc/chain_1.png deleted file mode 100644 index fe9e3d6ed..000000000 Binary files a/chain/etc/chain_1.png and /dev/null differ diff --git a/dao/src/main/java/com/iluwatar/dao/CustomException.java b/dao/src/main/java/com/iluwatar/dao/CustomException.java new file mode 100644 index 000000000..fd291b605 --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/CustomException.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +/** + * + * Custom exception + * + */ +public class CustomException extends Exception { + + private static final long serialVersionUID = 1L; + + public CustomException() {} + + public CustomException(String message) { + super(message); + } + + public CustomException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java index 6e93207cc..fa2e411da 100644 --- a/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java +++ b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java @@ -36,12 +36,16 @@ import java.util.stream.StreamSupport; import javax.sql.DataSource; +import org.apache.log4j.Logger; + /** - * An implementation of {@link CustomerDao} that persists customers in RDBMS. + * An implementation of {@link CustomerDao} that persists customers in RDBMS. * */ public class DbCustomerDao implements CustomerDao { + private static final Logger LOGGER = Logger.getLogger(DbCustomerDao.class); + private final DataSource dataSource; /** @@ -65,8 +69,8 @@ public class DbCustomerDao implements CustomerDao { Connection connection; try { connection = getConnection(); - PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); //NOSONAR - ResultSet resultSet = statement.executeQuery(); //NOSONAR + PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); // NOSONAR + ResultSet resultSet = statement.executeQuery(); // NOSONAR return StreamSupport.stream(new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { @@ -79,12 +83,12 @@ public class DbCustomerDao implements CustomerDao { action.accept(createCustomer(resultSet)); return true; } catch (SQLException e) { - throw new RuntimeException(e); + throw new RuntimeException(e); // NOSONAR } } }, false).onClose(() -> mutedClose(connection, statement, resultSet)); } catch (SQLException e) { - throw new Exception(e.getMessage(), e); + throw new CustomException(e.getMessage(), e); } } @@ -98,7 +102,7 @@ public class DbCustomerDao implements CustomerDao { statement.close(); connection.close(); } catch (SQLException e) { - e.printStackTrace(); + LOGGER.info("Exception thrown " + e.getMessage()); } } @@ -113,19 +117,26 @@ public class DbCustomerDao implements CustomerDao { */ @Override public Optional getById(int id) throws Exception { + + ResultSet resultSet = null; + try (Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) { statement.setInt(1, id); - ResultSet resultSet = statement.executeQuery(); + resultSet = statement.executeQuery(); if (resultSet.next()) { return Optional.of(createCustomer(resultSet)); } else { return Optional.empty(); } } catch (SQLException ex) { - throw new Exception(ex.getMessage(), ex); + throw new CustomException(ex.getMessage(), ex); + } finally { + if (resultSet != null) { + resultSet.close(); + } } } @@ -147,7 +158,7 @@ public class DbCustomerDao implements CustomerDao { statement.execute(); return true; } catch (SQLException ex) { - throw new Exception(ex.getMessage(), ex); + throw new CustomException(ex.getMessage(), ex); } } @@ -164,7 +175,7 @@ public class DbCustomerDao implements CustomerDao { statement.setInt(3, customer.getId()); return statement.executeUpdate() > 0; } catch (SQLException ex) { - throw new Exception(ex.getMessage(), ex); + throw new CustomException(ex.getMessage(), ex); } } @@ -179,7 +190,7 @@ public class DbCustomerDao implements CustomerDao { statement.setInt(1, customer.getId()); return statement.executeUpdate() > 0; } catch (SQLException ex) { - throw new Exception(ex.getMessage(), ex); + throw new CustomException(ex.getMessage(), ex); } } } diff --git a/partial-response/README.md b/partial-response/README.md new file mode 100644 index 000000000..5d03cb359 --- /dev/null +++ b/partial-response/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Partial Response +folder: partial-response +permalink: /patterns/partial-response/ +categories: Architectural +tags: + - Java + - KISS + - YAGNI + - Difficulty-Beginner +--- + +## Intent +Send partial response from server to client on need basis. Client will specify the the fields +that it need to server, instead of serving all details for resource. + +![alt text](./etc/partial-response.urm.png "partial-response") + +## Applicability +Use the Partial Response pattern when + +* Client need only subset of data from resource. +* To avoid too much data transfer over wire + +## Credits + +* [Common Design Patterns](https://cloud.google.com/apis/design/design_patterns) +* [Partial Response in RESTful API Design](http://yaoganglian.com/2013/07/01/partial-response/) diff --git a/partial-response/etc/partial-response.ucls b/partial-response/etc/partial-response.ucls new file mode 100644 index 000000000..a0b029496 --- /dev/null +++ b/partial-response/etc/partial-response.ucls @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/partial-response/etc/partial-response.urm.png b/partial-response/etc/partial-response.urm.png new file mode 100644 index 000000000..17dbd5f1b Binary files /dev/null and b/partial-response/etc/partial-response.urm.png differ diff --git a/partial-response/etc/partial-response.urm.puml b/partial-response/etc/partial-response.urm.puml new file mode 100644 index 000000000..69efd0454 --- /dev/null +++ b/partial-response/etc/partial-response.urm.puml @@ -0,0 +1,31 @@ +@startuml +package com.iluwatar.partialresponse { + class FieldJsonMapper { + + FieldJsonMapper() + - getString(video : Video, declaredField : Field) : String + + toJson(video : Video, fields : String[]) : String + } + class Video { + - description : String + - director : String + - id : Integer + - language : String + - length : Integer + - title : String + + Video(id : Integer, title : String, length : Integer, description : String, director : String, language : String) + + toString() : String + } + class VideoClientApp { + - LOGGER : Logger {static} + + VideoClientApp() + + main(args : String[]) {static} + } + class VideoResource { + - fieldJsonMapper : FieldJsonMapper + - videos : Map + + VideoResource(fieldJsonMapper : FieldJsonMapper, videos : Map) + + getDetails(id : Integer, fields : String[]) : String + } +} +VideoResource --> "-fieldJsonMapper" FieldJsonMapper +@enduml \ No newline at end of file diff --git a/partial-response/pom.xml b/partial-response/pom.xml new file mode 100644 index 000000000..ed685eb6a --- /dev/null +++ b/partial-response/pom.xml @@ -0,0 +1,49 @@ + + + + + java-design-patterns + com.iluwatar + 1.17.0-SNAPSHOT + + 4.0.0 + + partial-response + + + junit + junit + + + org.mockito + mockito-core + + + + + \ No newline at end of file diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/App.java b/partial-response/src/main/java/com/iluwatar/partialresponse/App.java new file mode 100644 index 000000000..2977b50a7 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/App.java @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * The Partial response pattern is a design pattern in which client specifies fields to fetch to serve. + * Here {@link App} is playing as client for {@link VideoResource} server. + * Client ask for specific fields information in video to server. + *

+ *

+ * {@link VideoResource} act as server to serve video information. + */ + +public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Method as act client and request to server for video details. + * + * @param args program argument. + */ + public static void main(String[] args) throws Exception { + Map videos = new HashMap<>(); + videos.put(1, new Video(1, "Avatar", 178, "epic science fiction film", "James Cameron", "English")); + videos.put(2, new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese")); + videos.put(3, new Video(3, "Interstellar", 169, "Adventure & Sci-Fi", "Christopher Nolan", "English")); + VideoResource videoResource = new VideoResource(new FieldJsonMapper(), videos); + + + LOGGER.info("Retrieving full response from server:-"); + LOGGER.info("Get all video information:"); + String videoDetails = videoResource.getDetails(1); + LOGGER.info(videoDetails); + + LOGGER.info("----------------------------------------------------------"); + + LOGGER.info("Retrieving partial response from server:-"); + LOGGER.info("Get video @id, @title, @director:"); + String specificFieldsDetails = videoResource.getDetails(3, "id", "title", "director"); + LOGGER.info(specificFieldsDetails); + + LOGGER.info("Get video @id, @length:"); + String videoLength = videoResource.getDetails(3, "id", "length"); + LOGGER.info(videoLength); + } +} diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java b/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java new file mode 100644 index 000000000..9283cfa69 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +import java.lang.reflect.Field; + +/** + * Map a video to json + */ +public class FieldJsonMapper { + + /** + * @param video object containing video information + * @param fields fields information to get + * @return json of required fields from video + */ + public String toJson(Video video, String[] fields) throws Exception { + StringBuilder json = new StringBuilder().append("{"); + + for (int i = 0, fieldsLength = fields.length; i < fieldsLength; i++) { + json.append(getString(video, Video.class.getDeclaredField(fields[i]))); + if (i != fieldsLength - 1) { + json.append(","); + } + } + json.append("}"); + return json.toString(); + } + + private String getString(Video video, Field declaredField) throws IllegalAccessException { + declaredField.setAccessible(true); + Object value = declaredField.get(video); + if (declaredField.get(video) instanceof Integer) { + return "\"" + declaredField.getName() + "\"" + ": " + value; + } + return "\"" + declaredField.getName() + "\"" + ": " + "\"" + value.toString() + "\""; + } +} diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java b/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java new file mode 100644 index 000000000..e242965e3 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java @@ -0,0 +1,70 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +/** + * {@link Video} is a entity to serve from server.It contains all video related information.. + *

+ */ +public class Video { + private final Integer id; + private final String title; + private final Integer length; + private final String description; + private final String director; + private final String language; + + /** + * @param id video unique id + * @param title video title + * @param length video length in minutes + * @param description video description by publisher + * @param director video director name + * @param language video language {private, public} + */ + public Video(Integer id, String title, Integer length, String description, String director, String language) { + this.id = id; + this.title = title; + this.length = length; + this.description = description; + this.director = director; + this.language = language; + } + + /** + * @return json representaion of video + */ + @Override + public String toString() { + return "{" + + "\"id\": " + id + "," + + "\"title\": \"" + title + "\"," + + "\"length\": " + length + "," + + "\"description\": \"" + description + "\"," + + "\"director\": \"" + director + "\"," + + "\"language\": \"" + language + "\"," + + "}"; + } +} diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java b/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java new file mode 100644 index 000000000..2bf7a73c1 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java @@ -0,0 +1,57 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +import java.util.Map; + +/** + * The resource class which serves video information. + * This class act as server in the demo. Which has all video details. + */ +public class VideoResource { + private FieldJsonMapper fieldJsonMapper; + private Map videos; + + /** + * @param fieldJsonMapper map object to json. + * @param videos initialize resource with existing videos. Act as database. + */ + public VideoResource(FieldJsonMapper fieldJsonMapper, Map videos) { + this.fieldJsonMapper = fieldJsonMapper; + this.videos = videos; + } + + /** + * @param id video id + * @param fields fields to get information about + * @return full response if no fields specified else partial response for given field. + */ + public String getDetails(Integer id, String... fields) throws Exception { + if (fields.length == 0) { + return videos.get(id).toString(); + } + return fieldJsonMapper.toJson(videos.get(id), fields); + } +} diff --git a/partial-response/src/test/java/com/iluwatar/partialresponse/AppTest.java b/partial-response/src/test/java/com/iluwatar/partialresponse/AppTest.java new file mode 100644 index 000000000..2ac34dd0d --- /dev/null +++ b/partial-response/src/test/java/com/iluwatar/partialresponse/AppTest.java @@ -0,0 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void main() throws Exception { + String[] args = {}; + App.main(args); + } + +} \ No newline at end of file diff --git a/partial-response/src/test/java/com/iluwatar/partialresponse/FieldJsonMapperTest.java b/partial-response/src/test/java/com/iluwatar/partialresponse/FieldJsonMapperTest.java new file mode 100644 index 000000000..9bc078b57 --- /dev/null +++ b/partial-response/src/test/java/com/iluwatar/partialresponse/FieldJsonMapperTest.java @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * tests {@link FieldJsonMapper}. + */ +public class FieldJsonMapperTest { + private FieldJsonMapper mapper; + + @Before + public void setUp() { + mapper = new FieldJsonMapper(); + } + + @Test + public void shouldReturnJsonForSpecifiedFieldsInVideo() throws Exception { + String[] fields = new String[]{"id", "title", "length"}; + Video video = new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese"); + + String jsonFieldResponse = mapper.toJson(video, fields); + + String expectedDetails = "{\"id\": 2,\"title\": \"Godzilla Resurgence\",\"length\": 120}"; + assertEquals(expectedDetails, jsonFieldResponse); + } +} \ No newline at end of file diff --git a/partial-response/src/test/java/com/iluwatar/partialresponse/VideoResourceTest.java b/partial-response/src/test/java/com/iluwatar/partialresponse/VideoResourceTest.java new file mode 100644 index 000000000..0453e8ffd --- /dev/null +++ b/partial-response/src/test/java/com/iluwatar/partialresponse/VideoResourceTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * 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.partialresponse; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +/** + * tests {@link VideoResource}. + */ +@RunWith(MockitoJUnitRunner.class) +public class VideoResourceTest { + @Mock + private FieldJsonMapper fieldJsonMapper; + + private VideoResource resource; + + @Before + public void setUp() { + Map videos = new HashMap<>(); + videos.put(1, new Video(1, "Avatar", 178, "epic science fiction film", "James Cameron", "English")); + videos.put(2, new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese")); + videos.put(3, new Video(3, "Interstellar", 169, "Adventure & Sci-Fi", "Christopher Nolan", "English")); + resource = new VideoResource(fieldJsonMapper, videos); + } + + @Test + public void shouldGiveVideoDetailsById() throws Exception { + String actualDetails = resource.getDetails(1); + + String expectedDetails = "{\"id\": 1,\"title\": \"Avatar\",\"length\": 178,\"description\": " + + "\"epic science fiction film\",\"director\": \"James Cameron\",\"language\": \"English\",}"; + assertEquals(expectedDetails, actualDetails); + } + + @Test + public void shouldGiveSpecifiedFieldsInformationOfVideo() throws Exception { + String[] fields = new String[]{"id", "title", "length"}; + + String expectedDetails = "{\"id\": 1,\"title\": \"Avatar\",\"length\": 178}"; + when(fieldJsonMapper.toJson(any(Video.class), eq(fields))).thenReturn(expectedDetails); + + String actualFieldsDetails = resource.getDetails(2, fields); + + assertEquals(expectedDetails, actualFieldsDetails); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index df3da6e3d..42dc54145 100644 --- a/pom.xml +++ b/pom.xml @@ -145,8 +145,9 @@ cqrs event-sourcing data-transfer-object - throttling + throttling unit-of-work + partial-response @@ -474,6 +475,8 @@ decorator facade flyweight + proxy + chain diff --git a/proxy/etc/proxy.png b/proxy/etc/proxy.png deleted file mode 100644 index 300e58dd3..000000000 Binary files a/proxy/etc/proxy.png and /dev/null differ diff --git a/proxy/etc/proxy.ucls b/proxy/etc/proxy.ucls deleted file mode 100644 index 76b5f0160..000000000 --- a/proxy/etc/proxy.ucls +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/proxy/etc/proxy.urm.puml b/proxy/etc/proxy.urm.puml deleted file mode 100644 index ffe0fa446..000000000 --- a/proxy/etc/proxy.urm.puml +++ /dev/null @@ -1,32 +0,0 @@ -@startuml -package com.iluwatar.proxy { - class App { - + App() - + main(args : String[]) {static} - } - class IvoryTower { - - LOGGER : Logger {static} - + IvoryTower() - + enter(wizard : Wizard) - } - class Wizard { - - name : String - + Wizard(name : String) - + toString() : String - } - interface WizardTower { - + enter(Wizard) {abstract} - } - class WizardTowerProxy { - - LOGGER : Logger {static} - - NUM_WIZARDS_ALLOWED : int {static} - - numWizards : int - - tower : WizardTower - + WizardTowerProxy(tower : WizardTower) - + enter(wizard : Wizard) - } -} -WizardTowerProxy --> "-tower" WizardTower -IvoryTower ..|> WizardTower -WizardTowerProxy ..|> WizardTower -@enduml \ No newline at end of file