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()) { %> + + <% } else { %> + + <% } %> + + + + <% if(propertiesBean.isBusinessInterest()) { %> + + <% } else { %> + + <% } %> + + <% if(propertiesBean.isSportsInterest()) { %> + + <% } else { %> + + <% } %> + + + + <% if(propertiesBean.isScienceNewsInterest()) { %> + + <% } else { %> + + <% } %> + + +
<%@include file="worldNews.jsp"%><%@include file="localNews.jsp"%>
<%@include file="businessNews.jsp"%><%@include file="localNews.jsp"%><%@include file="sportsNews.jsp"%><%@include file="localNews.jsp"%>
<%@include file="scienceNews.jsp"%><%@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 worldNew tech companies to invest in
Industry leaders unveil new projectPrice fluctuations and what they mean
+ + +``` +`localNews.jsp` +```html + + +
+

+ Generic Local News +

+ +
+ + +``` +The results are as such: + +1) The user has put their name as `Tammy` in the request parameters and no preferences: +![alt text](etc/images/noparam.PNG) +2) The user has put their name as `Johnny` in the request parameters and has a preference for world, business, and science news: +![alt text](etc/images/threeparams.PNG) + +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 + +![alt text](./etc/composite_view.png) + +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 worldNew tech companies to invest in
Industry leaders unveil new projectPrice 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 +

+ +
+ + 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()) { %> + + <% } else { %> + + <% } %> + + + + <% if(propertiesBean.isBusinessInterest()) { %> + + <% } else { %> + + <% } %> + + <% if(propertiesBean.isSportsInterest()) { %> + + <% } else { %> + + <% } %> + + + + <% if(propertiesBean.isScienceNewsInterest()) { %> + + <% } else { %> + + <% } %> + + +
<%@include file="worldNews.jsp"%><%@include file="localNews.jsp"%>
<%@include file="businessNews.jsp"%><%@include file="localNews.jsp"%><%@include file="sportsNews.jsp"%><%@include file="localNews.jsp"%>
<%@include file="scienceNews.jsp"%><%@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 +

+ +
+ + 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