diff --git a/composite-view/README.md b/composite-view/README.md
new file mode 100644
index 000000000..e4ccced28
--- /dev/null
+++ b/composite-view/README.md
@@ -0,0 +1,325 @@
+---
+layout: pattern
+title: Composite View
+folder: composite-view
+permalink: /patterns/composite-view/
+categories: Structural
+language: en
+tags:
+- Enterprise Integration Pattern
+- Presentation
+---
+
+## Name
+**Composite View**
+
+## Intent
+The purpose of the Composite View Pattern is to increase re-usability and flexibility when creating views for websites/webapps.
+This pattern seeks to decouple the content of the page from its layout, allowing changes to be made to either the content
+or layout of the page without impacting the other. This pattern also allows content to be easily reused across different views easily.
+
+## Explanation
+Real World Example
+> A news site wants to display the current date and news to different users
+> based on that user's preferences. The news site will substitute in different news feed
+> components depending on the user's interest, defaulting to local news.
+
+In Plain Words
+> Composite View Pattern is having a main view being composed of smaller subviews.
+> The layout of this composite view is based on a template. A View-manager then decides which
+> subviews to include in this template.
+
+Wikipedia Says
+> Composite views that are composed of multiple atomic subviews. Each component of
+> the template may be included dynamically into the whole and the layout of the page may be managed independently of the content.
+> This solution provides for the creation of a composite view based on the inclusion and substitution of
+> modular dynamic and static template fragments.
+> It promotes the reuse of atomic portions of the view by encouraging modular design.
+
+**Programmatic Example**
+
+Since this is a web development pattern, a server is required to demonstrate it.
+This example uses Tomcat 10.0.13 to run the servlet, and this programmatic example will only work with Tomcat 10+.
+
+Firstly there is `AppServlet` which is an `HttpServlet` that runs on Tomcat 10+.
+```java
+public class AppServlet extends HttpServlet {
+ private String msgPartOne = "
This Server Doesn't Support";
+ private String msgPartTwo = "Requests
\n"
+ + "Use a GET request with boolean values for the following parameters\n"
+ + "'name'
\n'bus'
\n'sports'
\n'sci'
\n'world'
";
+
+ private String destination = "newsDisplay.jsp";
+
+ public AppServlet() {
+
+ }
+
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
+ ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
+ req.setAttribute("properties", reqParams);
+ requestDispatcher.forward(req, resp);
+ }
+
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/html");
+ PrintWriter out = resp.getWriter();
+ out.println(msgPartOne + " Post " + msgPartTwo);
+
+ }
+
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/html");
+ PrintWriter out = resp.getWriter();
+ out.println(msgPartOne + " Delete " + msgPartTwo);
+
+ }
+
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/html");
+ PrintWriter out = resp.getWriter();
+ out.println(msgPartOne + " Put " + msgPartTwo);
+
+ }
+}
+
+```
+This servlet is not part of the pattern, and simply forwards GET requests to the correct JSP.
+PUT, POST, and DELETE requests are not supported and will simply show an error message.
+
+The view management in this example is done via a javabean class: `ClientPropertiesBean`, which stores user preferences.
+```java
+public class ClientPropertiesBean implements Serializable {
+
+ private static final String WORLD_PARAM = "world";
+ private static final String SCIENCE_PARAM = "sci";
+ private static final String SPORTS_PARAM = "sport";
+ private static final String BUSINESS_PARAM = "bus";
+ private static final String NAME_PARAM = "name";
+
+ private static final String DEFAULT_NAME = "DEFAULT_NAME";
+ private boolean worldNewsInterest;
+ private boolean sportsInterest;
+ private boolean businessInterest;
+ private boolean scienceNewsInterest;
+ private String name;
+
+ public ClientPropertiesBean() {
+ worldNewsInterest = true;
+ sportsInterest = true;
+ businessInterest = true;
+ scienceNewsInterest = true;
+ name = DEFAULT_NAME;
+
+ }
+
+ public ClientPropertiesBean(HttpServletRequest req) {
+ worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
+ sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
+ businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
+ scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
+ String tempName = req.getParameter(NAME_PARAM);
+ if (tempName == null || tempName == "") {
+ tempName = DEFAULT_NAME;
+ }
+ name = tempName;
+ }
+ // getters and setters generated by Lombok
+}
+```
+This javabean has a default constructor, and another that takes an `HttpServletRequest`.
+This second constructor takes the request object, parses out the request parameters which contain the
+user preferences for different types of news.
+
+The template for the news page is in `newsDisplay.jsp`
+```html
+
+
+
+
+
+ <%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
+ Welcome <%= propertiesBean.getName()%>
+
+
+
+
+ |
+ <% if(propertiesBean.isWorldNewsInterest()) { %>
+ <%@include file="worldNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+ |
+
+
+ <% if(propertiesBean.isBusinessInterest()) { %>
+ <%@include file="businessNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+ |
+ <% if(propertiesBean.isSportsInterest()) { %>
+ <%@include file="sportsNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+
+
+ |
+ <% if(propertiesBean.isScienceNewsInterest()) { %>
+ <%@include file="scienceNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+ |
+
+
+
+
+```
+This JSP page is the template. It declares a table with three rows, with one component in the first row,
+two components in the second row, and one component in the third row.
+
+The scriplets in the file are part of the
+view management strategy that include different atomic subviews based on the user preferences in the Javabean.
+
+Here are two examples of the mock atomic subviews used in the composite:
+`businessNews.jsp`
+```html
+
+
+
+
+
+
+ Generic Business News
+
+
+
+ Stock prices up across the world |
+ New tech companies to invest in |
+
+
+ Industry leaders unveil new project |
+ Price fluctuations and what they mean |
+
+
+
+
+```
+`localNews.jsp`
+```html
+
+
+
+
+ Generic Local News
+
+
+ -
+ Mayoral elections coming up in 2 weeks
+
+ -
+ New parking meter rates downtown coming tomorrow
+
+ -
+ Park renovations to finish by the next year
+
+ -
+ Annual marathon sign ups available online
+
+
+
+
+
+```
+The results are as such:
+
+1) The user has put their name as `Tammy` in the request parameters and no preferences:
+
+2) The user has put their name as `Johnny` in the request parameters and has a preference for world, business, and science news:
+
+
+The different subviews such as `worldNews.jsp`, `businessNews.jsp`, etc. are included conditionally
+based on the request parameters.
+
+**How To Use**
+
+To try this example, make sure you have Tomcat 10+ installed.
+Set up your IDE to build a WAR file from the module and deploy that file to the server
+
+IntelliJ:
+
+Under `Run` and `edit configurations` Make sure Tomcat server is one of the run configurations.
+Go to the deployment tab, and make sure there is one artifact being built called `composite-view:war exploded`.
+If not present, add one.
+
+Ensure that the artifact is being built from the content of the `web` directory and the compilation results of the module.
+Point the output of the artifact to a convenient place. Run the configuration and view the landing page,
+follow instructions on that page to continue.
+
+## Class diagram
+
+
+
+The class diagram here displays the Javabean which is the view manager.
+The views are JSP's held inside the web directory.
+
+## Applicability
+
+This pattern is applicable to most websites that require content to be displayed dynamically/conditionally.
+If there are components that need to be re-used for multiple views, or if the project requires reusing a template,
+or if it needs to include content depending on certain conditions, then this pattern is a good choice.
+
+## Known uses
+
+Most modern websites use composite views in some shape or form, as they have templates for views and small atomic components
+that are included in the page dynamically. Most modern Javascript libraries, like React, support this design pattern
+with components.
+
+## Consequences
+**Pros**
+* Easy to re-use components
+* Change layout/content without affecting the other
+* Reduce code duplication
+* Code is more maintainable and modular
+
+**Cons**
+* Overhead cost at runtime
+* Slower response compared to directly embedding elements
+* Increases potential for display errors
+
+## Related patterns
+* [Composite (GoF)](https://java-design-patterns.com/patterns/composite/)
+* [View Helper](https://www.oracle.com/java/technologies/viewhelper.html)
+
+## Credits
+* [Core J2EE Patterns - Composite View](https://www.oracle.com/java/technologies/composite-view.html)
+* [Composite View Design Pattern – Core J2EE Patterns](https://www.dineshonjava.com/composite-view-design-pattern/)
+
diff --git a/composite-view/etc/composite-view.urm.puml b/composite-view/etc/composite-view.urm.puml
new file mode 100644
index 000000000..e92b13ea5
--- /dev/null
+++ b/composite-view/etc/composite-view.urm.puml
@@ -0,0 +1,29 @@
+@startuml
+package com.iluwatar.compositeview {
+ class ClientPropertiesBean {
+ - BUSINESS_PARAM : String {static}
+ - DEFAULT_NAME : String {static}
+ - NAME_PARAM : String {static}
+ - SCIENCE_PARAM : String {static}
+ - SPORTS_PARAM : String {static}
+ - WORLD_PARAM : String {static}
+ - businessInterest : boolean
+ - name : String
+ - scienceNewsInterest : boolean
+ - sportsInterest : boolean
+ - worldNewsInterest : boolean
+ + ClientPropertiesBean()
+ + ClientPropertiesBean(req : HttpServletRequest)
+ + getName() : String
+ + isBusinessInterest() : boolean
+ + isScienceNewsInterest() : boolean
+ + isSportsInterest() : boolean
+ + isWorldNewsInterest() : boolean
+ + setBusinessInterest(businessInterest : boolean)
+ + setName(name : String)
+ + setScienceNewsInterest(scienceNewsInterest : boolean)
+ + setSportsInterest(sportsInterest : boolean)
+ + setWorldNewsInterest(worldNewsInterest : boolean)
+ }
+}
+@enduml
\ No newline at end of file
diff --git a/composite-view/etc/composite_view.png b/composite-view/etc/composite_view.png
new file mode 100644
index 000000000..66215d941
Binary files /dev/null and b/composite-view/etc/composite_view.png differ
diff --git a/composite-view/etc/images/noparam.PNG b/composite-view/etc/images/noparam.PNG
new file mode 100644
index 000000000..2979cf2bc
Binary files /dev/null and b/composite-view/etc/images/noparam.PNG differ
diff --git a/composite-view/etc/images/threeparams.PNG b/composite-view/etc/images/threeparams.PNG
new file mode 100644
index 000000000..9338dedb8
Binary files /dev/null and b/composite-view/etc/images/threeparams.PNG differ
diff --git a/composite-view/pom.xml b/composite-view/pom.xml
new file mode 100644
index 000000000..cbb6159b8
--- /dev/null
+++ b/composite-view/pom.xml
@@ -0,0 +1,80 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ composite-view
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ junit
+ junit
+ test
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ 5.0.0
+ compile
+
+
+ org.mockito
+ mockito-core
+ 4.1.0
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.compositeview.App
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/composite-view/src/main/java/com/iluwatar/compositeview/AppServlet.java b/composite-view/src/main/java/com/iluwatar/compositeview/AppServlet.java
new file mode 100644
index 000000000..c15e44c9c
--- /dev/null
+++ b/composite-view/src/main/java/com/iluwatar/compositeview/AppServlet.java
@@ -0,0 +1,64 @@
+package com.iluwatar.compositeview;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * A servlet object that extends HttpServlet.
+ * Runs on Tomcat 10 and handles Http requests
+ */
+
+public final class AppServlet extends HttpServlet {
+ private String msgPartOne = "This Server Doesn't Support";
+ private String msgPartTwo = "Requests
\n"
+ + "Use a GET request with boolean values for the following parameters\n"
+ + "'name'
\n'bus'
\n'sports'
\n'sci'
\n'world'
";
+
+ private String destination = "newsDisplay.jsp";
+
+ public AppServlet() {
+
+ }
+
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
+ ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
+ req.setAttribute("properties", reqParams);
+ requestDispatcher.forward(req, resp);
+ }
+
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/html");
+ try (PrintWriter out = resp.getWriter()) {
+ out.println(msgPartOne + " Post " + msgPartTwo);
+ }
+
+ }
+
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/html");
+ try (PrintWriter out = resp.getWriter()) {
+ out.println(msgPartOne + " Delete " + msgPartTwo);
+ }
+ }
+
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/html");
+ try (PrintWriter out = resp.getWriter()) {
+ out.println(msgPartOne + " Put " + msgPartTwo);
+ }
+ }
+}
diff --git a/composite-view/src/main/java/com/iluwatar/compositeview/ClientPropertiesBean.java b/composite-view/src/main/java/com/iluwatar/compositeview/ClientPropertiesBean.java
new file mode 100644
index 000000000..c8e694713
--- /dev/null
+++ b/composite-view/src/main/java/com/iluwatar/compositeview/ClientPropertiesBean.java
@@ -0,0 +1,53 @@
+package com.iluwatar.compositeview;
+
+import jakarta.servlet.http.HttpServletRequest;
+import java.io.Serializable;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+
+/**
+ * A Java beans class that parses a http request and stores parameters.
+ * Java beans used in JSP's to dynamically include elements in view.
+ * DEFAULT_NAME = a constant, default name to be used for the default constructor
+ * worldNewsInterest = whether current request has world news interest
+ * sportsInterest = whether current request has a sportsInterest
+ * businessInterest = whether current request has a businessInterest
+ * scienceNewsInterest = whether current request has a scienceNewsInterest
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class ClientPropertiesBean implements Serializable {
+
+ private static final String WORLD_PARAM = "world";
+ private static final String SCIENCE_PARAM = "sci";
+ private static final String SPORTS_PARAM = "sport";
+ private static final String BUSINESS_PARAM = "bus";
+ private static final String NAME_PARAM = "name";
+
+ private static final String DEFAULT_NAME = "DEFAULT_NAME";
+ private boolean worldNewsInterest = true;
+ private boolean sportsInterest = true;
+ private boolean businessInterest = true;
+ private boolean scienceNewsInterest = true;
+ private String name = DEFAULT_NAME;
+
+ /**
+ * Constructor that parses an HttpServletRequest and stores all the request parameters.
+ *
+ * @param req the HttpServletRequest object that is passed in
+ */
+ public ClientPropertiesBean(HttpServletRequest req) {
+ worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
+ sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
+ businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
+ scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
+ String tempName = req.getParameter(NAME_PARAM);
+ if (tempName == null || tempName.equals("")) {
+ tempName = DEFAULT_NAME;
+ }
+ name = tempName;
+ }
+}
diff --git a/composite-view/src/test/java/com/iluwatar/compositeview/AppServletTest.java b/composite-view/src/test/java/com/iluwatar/compositeview/AppServletTest.java
new file mode 100644
index 000000000..38c1a1bc2
--- /dev/null
+++ b/composite-view/src/test/java/com/iluwatar/compositeview/AppServletTest.java
@@ -0,0 +1,83 @@
+package com.iluwatar.compositeview;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import static org.junit.Assert.*;
+
+/* Written with reference from https://stackoverflow.com/questions/5434419/how-to-test-my-servlet-using-junit
+and https://stackoverflow.com/questions/50211433/servlets-unit-testing
+ */
+
+public class AppServletTest extends Mockito{
+ private String msgPartOne = "This Server Doesn't Support";
+ private String msgPartTwo = "Requests
\n"
+ + "Use a GET request with boolean values for the following parameters\n"
+ + "'name'
\n'bus'
\n'sports'
\n'sci'
\n'world'
";
+ private String destination = "newsDisplay.jsp";
+
+ @Test
+ public void testDoGet() throws Exception {
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
+ RequestDispatcher mockDispatcher = Mockito.mock(RequestDispatcher.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ when(mockResp.getWriter()).thenReturn(printWriter);
+ when(mockReq.getRequestDispatcher(destination)).thenReturn(mockDispatcher);
+ AppServlet curServlet = new AppServlet();
+ curServlet.doGet(mockReq, mockResp);
+ verify(mockReq, times(1)).getRequestDispatcher(destination);
+ verify(mockDispatcher).forward(mockReq, mockResp);
+
+
+ }
+
+ @Test
+ public void testDoPost() throws Exception {
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ when(mockResp.getWriter()).thenReturn(printWriter);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doPost(mockReq, mockResp);
+ printWriter.flush();
+ assertTrue(stringWriter.toString().contains(msgPartOne + " Post " + msgPartTwo));
+ }
+
+ @Test
+ public void testDoPut() throws Exception {
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ when(mockResp.getWriter()).thenReturn(printWriter);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doPut(mockReq, mockResp);
+ printWriter.flush();
+ assertTrue(stringWriter.toString().contains(msgPartOne + " Put " + msgPartTwo));
+ }
+
+ @Test
+ public void testDoDelete() throws Exception {
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ when(mockResp.getWriter()).thenReturn(printWriter);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doDelete(mockReq, mockResp);
+ printWriter.flush();
+ assertTrue(stringWriter.toString().contains(msgPartOne + " Delete " + msgPartTwo));
+ }
+}
diff --git a/composite-view/src/test/java/com/iluwatar/compositeview/JavaBeansTest.java b/composite-view/src/test/java/com/iluwatar/compositeview/JavaBeansTest.java
new file mode 100644
index 000000000..6583ab45d
--- /dev/null
+++ b/composite-view/src/test/java/com/iluwatar/compositeview/JavaBeansTest.java
@@ -0,0 +1,71 @@
+package com.iluwatar.compositeview;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.Assert.*;
+
+public class JavaBeansTest {
+ @Test
+ public void testDefaultConstructor() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertEquals("DEFAULT_NAME", newBean.getName());
+ assertTrue(newBean.isBusinessInterest());
+ assertTrue(newBean.isScienceNewsInterest());
+ assertTrue(newBean.isSportsInterest());
+ assertTrue(newBean.isWorldNewsInterest());
+
+ }
+
+ @Test
+ public void testNameGetterSetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertEquals("DEFAULT_NAME", newBean.getName());
+ newBean.setName("TEST_NAME_ONE");
+ assertEquals("TEST_NAME_ONE", newBean.getName());
+ }
+
+ @Test
+ public void testBusinessSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isBusinessInterest());
+ newBean.setBusinessInterest(false);
+ assertFalse(newBean.isBusinessInterest());
+ }
+
+ @Test
+ public void testScienceSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isScienceNewsInterest());
+ newBean.setScienceNewsInterest(false);
+ assertFalse(newBean.isScienceNewsInterest());
+ }
+
+ @Test
+ public void testSportsSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isSportsInterest());
+ newBean.setSportsInterest(false);
+ assertFalse(newBean.isSportsInterest());
+ }
+
+ @Test
+ public void testWorldSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isWorldNewsInterest());
+ newBean.setWorldNewsInterest(false);
+ assertFalse(newBean.isWorldNewsInterest());
+ }
+
+ @Test
+ public void testRequestConstructor(){
+ HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
+ ClientPropertiesBean newBean = new ClientPropertiesBean((mockReq));
+ assertEquals("DEFAULT_NAME", newBean.getName());
+ assertFalse(newBean.isWorldNewsInterest());
+ assertFalse(newBean.isBusinessInterest());
+ assertFalse(newBean.isScienceNewsInterest());
+ assertFalse(newBean.isSportsInterest());
+ }
+}
diff --git a/composite-view/web/WEB-INF/web.xml b/composite-view/web/WEB-INF/web.xml
new file mode 100644
index 000000000..7cdd74c7b
--- /dev/null
+++ b/composite-view/web/WEB-INF/web.xml
@@ -0,0 +1,14 @@
+
+
+
+ appServlet
+ com.iluwatar.compositeview.AppServlet
+
+
+ appServlet
+ /news
+
+
\ No newline at end of file
diff --git a/composite-view/web/businessNews.jsp b/composite-view/web/businessNews.jsp
new file mode 100644
index 000000000..f9c67bc3c
--- /dev/null
+++ b/composite-view/web/businessNews.jsp
@@ -0,0 +1,33 @@
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 2:51 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+
+ Generic Business News
+
+
+
+ Stock prices up across the world |
+ New tech companies to invest in |
+
+
+ Industry leaders unveil new project |
+ Price fluctuations and what they mean |
+
+
+
+
diff --git a/composite-view/web/header.jsp b/composite-view/web/header.jsp
new file mode 100644
index 000000000..07b24f878
--- /dev/null
+++ b/composite-view/web/header.jsp
@@ -0,0 +1,23 @@
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 1:28 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ page import="java.util.Date"%>
+
+
+
+
+
+ <% String todayDateStr = (new Date().toString()); %>
+ Today's Personalized Frontpage
+ <%=todayDateStr%>
+
+
diff --git a/composite-view/web/index.jsp b/composite-view/web/index.jsp
new file mode 100644
index 000000000..527db3b33
--- /dev/null
+++ b/composite-view/web/index.jsp
@@ -0,0 +1,20 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+ Welcome To The Composite Patterns Mock News Site
+ Send a GET request to the "/news" path to see the composite view with mock news
+ Use the following parameters:
+ name: string name to be dynamically displayed
+ bus: boolean for whether you want to see the mock business news
+ world: boolean for whether you want to see the mock world news
+ sci: boolean for whether you want to see the mock world news
+ sport: boolean for whether you want to see the mock world news
+
+
diff --git a/composite-view/web/localNews.jsp b/composite-view/web/localNews.jsp
new file mode 100644
index 000000000..3ab3ea1e9
--- /dev/null
+++ b/composite-view/web/localNews.jsp
@@ -0,0 +1,25 @@
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+ Generic Local News
+
+
+ -
+ Mayoral elections coming up in 2 weeks
+
+ -
+ New parking meter rates downtown coming tomorrow
+
+ -
+ Park renovations to finish by the next year
+
+ -
+ Annual marathon sign ups available online
+
+
+
+
+
diff --git a/composite-view/web/newsDisplay.jsp b/composite-view/web/newsDisplay.jsp
new file mode 100644
index 000000000..936a98e1d
--- /dev/null
+++ b/composite-view/web/newsDisplay.jsp
@@ -0,0 +1,57 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ page import="com.iluwatar.compositeview.ClientPropertiesBean"%>
+
+
+
+
+
+ <%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
+ Welcome <%= propertiesBean.getName()%>
+
+
+
+
+ |
+ <% if(propertiesBean.isWorldNewsInterest()) { %>
+ <%@include file="worldNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+ |
+
+
+ <% if(propertiesBean.isBusinessInterest()) { %>
+ <%@include file="businessNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+ |
+ <% if(propertiesBean.isSportsInterest()) { %>
+ <%@include file="sportsNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+
+
+ |
+ <% if(propertiesBean.isScienceNewsInterest()) { %>
+ <%@include file="scienceNews.jsp"%> |
+ <% } else { %>
+ <%@include file="localNews.jsp"%> |
+ <% } %>
+ |
+
+
+
+
diff --git a/composite-view/web/scienceNews.jsp b/composite-view/web/scienceNews.jsp
new file mode 100644
index 000000000..2c81d8ff5
--- /dev/null
+++ b/composite-view/web/scienceNews.jsp
@@ -0,0 +1,34 @@
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 4:18 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+ Generic Science News
+
+
+ -
+ New model of gravity proposed for dark matter
+
+ -
+ Genetic modifying technique proved on bacteria
+
+ -
+ Neurology study maps brain with new precision
+
+ -
+ Survey of rainforest discovers 15 new species
+
+ -
+ New signalling pathway for immune system discovered
+
+
+
+
+
diff --git a/composite-view/web/sportsNews.jsp b/composite-view/web/sportsNews.jsp
new file mode 100644
index 000000000..3d3aa12e8
--- /dev/null
+++ b/composite-view/web/sportsNews.jsp
@@ -0,0 +1,32 @@
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 3:53 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+
+ Generic Sports News
+
+
+ International football match delayed due to weather, will be held next week
+
+
+ New rising stars in winter sports, ten new athletes that will shake up the scene
+
+
+ Biggest upset in basketball history, upstart team sweeps competition
+
+
+
diff --git a/composite-view/web/worldNews.jsp b/composite-view/web/worldNews.jsp
new file mode 100644
index 000000000..a75060d76
--- /dev/null
+++ b/composite-view/web/worldNews.jsp
@@ -0,0 +1,34 @@
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 2:51 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+
+ Generic World News
+
+
+
+ New trade talks happening at UN on Thursday |
+
+
+ European Union to announce new resolution next week |
+
+
+ UN delivers report on world economic status |
+
+
+
+
diff --git a/pom.xml b/pom.xml
index e1573a737..e58146c72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -228,6 +228,7 @@
lockable-object
fanout-fanin
domain-model
+ composite-view
metadata-mapping