From fd402fc876613282aaf855fd8fb5b92413af8522 Mon Sep 17 00:00:00 2001 From: Nakul Nambiar <39953357+nakul251197@users.noreply.github.com> Date: Thu, 29 Dec 2022 22:05:29 +1100 Subject: [PATCH] feature: #1288 Added The Client Session Design Pattern (#2155) * #1288 Add client session design pattern * #1288 Add license to files * Inserted README file for session_state_pattern * Added real-world example to client-session/README * #1288 Add server tests * #1288 Fixed code smells * #1288 Removed unused getter and setters * #1288 incorporated feedback * #1288 Added maven-assembly-plugin * #1288 Added more description Co-authored-by: Nakul Nambiar Co-authored-by: Denis --- client-session/README.md | 114 ++++++++++++++++++ client-session/etc/session_state_pattern.png | Bin 0 -> 50158 bytes client-session/pom.xml | 62 ++++++++++ .../java/com/iluwatar/client/session/App.java | 55 +++++++++ .../com/iluwatar/client/session/Request.java | 42 +++++++ .../com/iluwatar/client/session/Server.java | 65 ++++++++++ .../com/iluwatar/client/session/Session.java | 48 ++++++++ .../com/iluwatar/client/session/AppTest.java | 38 ++++++ .../iluwatar/client/session/ServerTest.java | 14 +++ pom.xml | 1 + 10 files changed, 439 insertions(+) create mode 100644 client-session/README.md create mode 100644 client-session/etc/session_state_pattern.png create mode 100644 client-session/pom.xml create mode 100644 client-session/src/main/java/com/iluwatar/client/session/App.java create mode 100644 client-session/src/main/java/com/iluwatar/client/session/Request.java create mode 100644 client-session/src/main/java/com/iluwatar/client/session/Server.java create mode 100644 client-session/src/main/java/com/iluwatar/client/session/Session.java create mode 100644 client-session/src/test/java/com/iluwatar/client/session/AppTest.java create mode 100644 client-session/src/test/java/com/iluwatar/client/session/ServerTest.java diff --git a/client-session/README.md b/client-session/README.md new file mode 100644 index 000000000..82204eb34 --- /dev/null +++ b/client-session/README.md @@ -0,0 +1,114 @@ +--- +title: Client Session Pattern +category: Architectural +language: en +tags: +- Decoupling +--- + +## Name + +[Client Session pattern](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client) + +## Intent + +- Create stateless servers that removes the problem of clustering, as users can switch between servers seamlessly. +- Makes data more resilient in case of server fail-over. +- Works well with smaller data sizes. + +## Explanation + +Real-World Example + +> You're looking to create a data management app allowing users to send requests to the server to +> modify and make changes to data stored on their devices. These requests are small in size and the +> data is individual to each user, negating the need for a large scale database implementation. +> Using the client session pattern, you are able to handle multiple concurrent requests, load +> balancing clients across different servers with ease due to servers remaining stateless. You also +> remove the need to store session IDs on the server side due to clients providing all the +> information that a server needs to perform their process. + +In Plain words + +> Instead of storing information about the current client and the information being accessed on the +> server, it is maintained client side only. Client has to send session data with each request to +> the server and has to send an updated state back to the client, which is stored on the clients +> machine. The server doesn't have to store the client information. +> ([ref](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client)) + +**Programmatic Example** + +Here is the sample code to describe the client-session pattern. In the below code we are first +creating an instance of the Server. This server instance will then be used to get Session objects +for two clients. As you can see from the code below the Session object can be used to store any +relevant information that are required by the server to process the client request. These session +objects will then be passed on with every Request to the server. The Request will have the Session +object that stores the relevant client details along with the required data for processing the +request. The session information in every request helps the server identify the client and process +the request accordingly. + +```java +public class App { + + public static void main(String[] args) { + var server = new Server("localhost", 8080); + var session1 = server.getSession("Session1"); + var session2 = server.getSession("Session2"); + var request1 = new Request("Data1", session1); + var request2 = new Request("Data2", session2); + server.process(request1); + server.process(request2); + } +} + +@Data +@AllArgsConstructor +public class Session { + + /** + * Session id. + */ + private String id; + + /** + * Client name. + */ + private String clientName; + +} + +@Data +@AllArgsConstructor +public class Request { + + private String data; + + private Session session; + +} +``` + +## Architecture Diagram + +![alt text](./etc/session_state_pattern.png "Session State Pattern") + +## Applicability + +Use the client state pattern when: + +- Processing smaller amounts of data to prevent larger requests and response sizes. +- Remove the need for servers to save client states. Doing so also removes the need to store session IDs. +- Clustering is an issue and needs to be avoided. Stateless servers allow clients to be easily distributed across servers. +- Creates resilience from data losses due to server fails. + +## Consequences + +- The server is stateless. Any compute API will not store any data. +- Struggles to deal with large amounts of data. Creates longer send and receive times due to larger amounts of session data to manage. +- Security. All data is stored on the client's machine. This means that any vulnerabilities on the clients side can expose all data being sent and received by the server. + + +## Credits + +- [Dzone - Practical PHP patterns](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-client) +- [Client Session State Design Pattern - Ram N Java](https://www.youtube.com/watch?v=ycOSj9g41pc) diff --git a/client-session/etc/session_state_pattern.png b/client-session/etc/session_state_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..f1e23be957669215ed6829e444cc31fa152e58e3 GIT binary patch literal 50158 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 + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + client-session + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.client.session.App + + + + + + + + + diff --git a/client-session/src/main/java/com/iluwatar/client/session/App.java b/client-session/src/main/java/com/iluwatar/client/session/App.java new file mode 100644 index 000000000..282d8a7f3 --- /dev/null +++ b/client-session/src/main/java/com/iluwatar/client/session/App.java @@ -0,0 +1,55 @@ +/* + * 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.client.session; + +/** + * The Client-Session pattern allows the session data to be stored on the client side and send this + * data to the server with each request. + * + *

In this example, The {@link Server} class represents the server that would process the + * incoming {@link Request} and also assign {@link Session} to a client. Here one instance of Server + * is created. The we create two sessions for two different clients. These sessions are then passed + * on to the server in the request along with the data. The server is then able to interpret the + * client based on the session associated with it. + *

+ */ +public class App { + + /** + * Program entry point. + * + * @param args Command line args + */ + public static void main(String[] args) { + var server = new Server("localhost", 8080); + var session1 = server.getSession("Session1"); + var session2 = server.getSession("Session2"); + var request1 = new Request("Data1", session1); + var request2 = new Request("Data2", session2); + server.process(request1); + server.process(request2); + } +} diff --git a/client-session/src/main/java/com/iluwatar/client/session/Request.java b/client-session/src/main/java/com/iluwatar/client/session/Request.java new file mode 100644 index 000000000..008011f18 --- /dev/null +++ b/client-session/src/main/java/com/iluwatar/client/session/Request.java @@ -0,0 +1,42 @@ +/* + * 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.client.session; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * The Request class which contains the Session details and data. + */ +@Data +@AllArgsConstructor +public class Request { + + private String data; + + private Session session; + +} diff --git a/client-session/src/main/java/com/iluwatar/client/session/Server.java b/client-session/src/main/java/com/iluwatar/client/session/Server.java new file mode 100644 index 000000000..6d9dc3dbc --- /dev/null +++ b/client-session/src/main/java/com/iluwatar/client/session/Server.java @@ -0,0 +1,65 @@ +/* + * 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.client.session; + +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +/** + * The Server class. The client communicates with the server and request processing and getting a new session. + */ +@Slf4j +@Data +@AllArgsConstructor +public class Server { + private String host; + + private int port; + + + /** + * Creates a new session. + * + * @param name name of the client + * + * @return Session Object + */ + public Session getSession(String name) { + return new Session(UUID.randomUUID().toString(), name); + } + + /** + * Processes a request based on the session. + * + * @param request Request object with data and Session + */ + public void process(Request request) { + LOGGER.info("Processing Request with client: " + request.getSession().getClientName() + " data: " + request.getData()); + } + +} diff --git a/client-session/src/main/java/com/iluwatar/client/session/Session.java b/client-session/src/main/java/com/iluwatar/client/session/Session.java new file mode 100644 index 000000000..bb9f7246c --- /dev/null +++ b/client-session/src/main/java/com/iluwatar/client/session/Session.java @@ -0,0 +1,48 @@ +/* + * 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.client.session; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * The Session class. Each client get assigned a Session which is then used for further communications. + */ +@Data +@AllArgsConstructor +public class Session { + + /** + * Session id. + */ + private String id; + + /** + * Client name. + */ + private String clientName; + +} diff --git a/client-session/src/test/java/com/iluwatar/client/session/AppTest.java b/client-session/src/test/java/com/iluwatar/client/session/AppTest.java new file mode 100644 index 000000000..0e33f74f4 --- /dev/null +++ b/client-session/src/test/java/com/iluwatar/client/session/AppTest.java @@ -0,0 +1,38 @@ +/* + * 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.client.session; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + +class AppTest { + + @Test + void appStartsWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java b/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java new file mode 100644 index 000000000..dc09441f0 --- /dev/null +++ b/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java @@ -0,0 +1,14 @@ +package com.iluwatar.client.session; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ServerTest { + + @Test + void checkGetSession() { + Server server = new Server("localhost", 8080); + Session session = server.getSession("Session"); + assertEquals("Session", session.getClientName()); + } +} diff --git a/pom.xml b/pom.xml index 09e4dea4f..90c4b2941 100644 --- a/pom.xml +++ b/pom.xml @@ -198,6 +198,7 @@ composite-view metadata-mapping service-to-worker + client-session embedded-value currying serialized-entity