new pattern: Issue#1264: Implemented Composite-View Pattern (#1923)
* initial commit, created package, README, pom, and directory structure. * Issue#1264, continue working on JavaBeans, added getters, setters, and private fields. Created test file for JavaBeans. * set up junit for tests folder. * Issue#1264, set up local server and added web-application framework to composite-view to allow the JSP to run on a local Tomcat container. Wrote unit tests for Java-bean class, working on JSP pages. * Issue#1264, Added forwarding functionality to servlet and main composite view page. * Issue#1264, Finished composite view template in newsDisplay.jsp and created atomic sub-view components in businessNews.jsp, header.jsp, localNews.jsp, scienceNews.jsp, sportsNews.jsp, worldNews.jsp. Composite view page renders correctly, atomic views are inserted in and substituted in the template page depending on request parameters. * Issue#1264, Added all views, updated README.md with documentation. * Issue#1264, updated README.md, moved images folder into etc folder. * Issue#1264, removed build artifacts from tracked files. * Issue#1264, updated README.md * Issue#1264, updated README.md * Issue#1264, removed unused import, made AppServlet class final, changed to .equals() for string comparison. * Issue#1264, in AppServlet, put the output writing into try blocks to ensure writers are closed. * Issue#1264, added tests for Servlet, coverage up to 100%, used lombok to reduce boilerplate setters and getter, updated README.md with better grammar, appropriate tags and links to related patterns. Updated pom.xml to get rid of superfluous lines. * Issue#1264, made changes as requested in README.md. Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
This commit is contained in:
parent
07ee94d671
commit
7652b11bca
325
composite-view/README.md
Normal file
325
composite-view/README.md
Normal file
@ -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 = "<h1>This Server Doesn't Support";
|
||||||
|
private String msgPartTwo = "Requests</h1>\n"
|
||||||
|
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
|
||||||
|
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
|
||||||
|
|
||||||
|
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
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h1 { text-align: center;}
|
||||||
|
h2 { text-align: center;}
|
||||||
|
h3 { text-align: center;}
|
||||||
|
.centerTable {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
table {border: 1px solid black;}
|
||||||
|
tr {text-align: center;}
|
||||||
|
td {text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
|
||||||
|
<h1>Welcome <%= propertiesBean.getName()%></h1>
|
||||||
|
<jsp:include page="header.jsp"></jsp:include>
|
||||||
|
<table class="centerTable">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<% if(propertiesBean.isWorldNewsInterest()) { %>
|
||||||
|
<td><%@include file="worldNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<% if(propertiesBean.isBusinessInterest()) { %>
|
||||||
|
<td><%@include file="businessNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
<td></td>
|
||||||
|
<% if(propertiesBean.isSportsInterest()) { %>
|
||||||
|
<td><%@include file="sportsNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<% if(propertiesBean.isScienceNewsInterest()) { %>
|
||||||
|
<td><%@include file="scienceNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
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
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h2 { text-align: center;}
|
||||||
|
table {border: 1px solid black;}
|
||||||
|
tr {text-align: center;}
|
||||||
|
td {text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>
|
||||||
|
Generic Business News
|
||||||
|
</h2>
|
||||||
|
<table style="margin-right: auto; margin-left: auto">
|
||||||
|
<tr>
|
||||||
|
<td>Stock prices up across the world</td>
|
||||||
|
<td>New tech companies to invest in</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Industry leaders unveil new project</td>
|
||||||
|
<td>Price fluctuations and what they mean</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
`localNews.jsp`
|
||||||
|
```html
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3>
|
||||||
|
Generic Local News
|
||||||
|
</h3>
|
||||||
|
<ul style="list-style-type: none">
|
||||||
|
<li>
|
||||||
|
Mayoral elections coming up in 2 weeks
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
New parking meter rates downtown coming tomorrow
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Park renovations to finish by the next year
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Annual marathon sign ups available online
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
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/)
|
||||||
|
|
29
composite-view/etc/composite-view.urm.puml
Normal file
29
composite-view/etc/composite-view.urm.puml
Normal file
@ -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
|
BIN
composite-view/etc/composite_view.png
Normal file
BIN
composite-view/etc/composite_view.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
composite-view/etc/images/noparam.PNG
Normal file
BIN
composite-view/etc/images/noparam.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
BIN
composite-view/etc/images/threeparams.PNG
Normal file
BIN
composite-view/etc/images/threeparams.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
80
composite-view/pom.xml
Normal file
80
composite-view/pom.xml
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
Copyright © 2014-2021 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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>composite-view</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<version>5.0.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<version>4.1.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>com.iluwatar.compositeview.App</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -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 = "<h1>This Server Doesn't Support";
|
||||||
|
private String msgPartTwo = "Requests</h1>\n"
|
||||||
|
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
|
||||||
|
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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 = "<h1>This Server Doesn't Support";
|
||||||
|
private String msgPartTwo = "Requests</h1>\n"
|
||||||
|
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
|
||||||
|
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
14
composite-view/web/WEB-INF/web.xml
Normal file
14
composite-view/web/WEB-INF/web.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
|
||||||
|
version="4.0">
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>appServlet</servlet-name>
|
||||||
|
<servlet-class>com.iluwatar.compositeview.AppServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>appServlet</servlet-name>
|
||||||
|
<url-pattern>/news</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
</web-app>
|
33
composite-view/web/businessNews.jsp
Normal file
33
composite-view/web/businessNews.jsp
Normal file
@ -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" %>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h2 { text-align: center;}
|
||||||
|
table {border: 1px solid black;}
|
||||||
|
tr {text-align: center;}
|
||||||
|
td {text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>
|
||||||
|
Generic Business News
|
||||||
|
</h2>
|
||||||
|
<table style="margin-right: auto; margin-left: auto">
|
||||||
|
<tr>
|
||||||
|
<td>Stock prices up across the world</td>
|
||||||
|
<td>New tech companies to invest in</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Industry leaders unveil new project</td>
|
||||||
|
<td>Price fluctuations and what they mean</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
23
composite-view/web/header.jsp
Normal file
23
composite-view/web/header.jsp
Normal file
@ -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"%>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h1 { text-align: center;}
|
||||||
|
h2 { text-align: center;}
|
||||||
|
h3 { text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<% String todayDateStr = (new Date().toString()); %>
|
||||||
|
<h1>Today's Personalized Frontpage</h1>
|
||||||
|
<h2><%=todayDateStr%></h2>
|
||||||
|
</body>
|
||||||
|
</html>
|
20
composite-view/web/index.jsp
Normal file
20
composite-view/web/index.jsp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h1 { text-align: center;}
|
||||||
|
h2 { text-align: center;}
|
||||||
|
h3 { text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Welcome To The Composite Patterns Mock News Site</h1>
|
||||||
|
<h2>Send a GET request to the "/news" path to see the composite view with mock news</h2>
|
||||||
|
<h2>Use the following parameters:</h2>
|
||||||
|
<h3>name: string name to be dynamically displayed</h3>
|
||||||
|
<h3>bus: boolean for whether you want to see the mock business news</h3>
|
||||||
|
<h3>world: boolean for whether you want to see the mock world news</h3>
|
||||||
|
<h3>sci: boolean for whether you want to see the mock world news</h3>
|
||||||
|
<h3>sport: boolean for whether you want to see the mock world news</h3>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
composite-view/web/localNews.jsp
Normal file
25
composite-view/web/localNews.jsp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3>
|
||||||
|
Generic Local News
|
||||||
|
</h3>
|
||||||
|
<ul style="list-style-type: none">
|
||||||
|
<li>
|
||||||
|
Mayoral elections coming up in 2 weeks
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
New parking meter rates downtown coming tomorrow
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Park renovations to finish by the next year
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Annual marathon sign ups available online
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
57
composite-view/web/newsDisplay.jsp
Normal file
57
composite-view/web/newsDisplay.jsp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||||||
|
<%@ page import="com.iluwatar.compositeview.ClientPropertiesBean"%>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h1 { text-align: center;}
|
||||||
|
h2 { text-align: center;}
|
||||||
|
h3 { text-align: center;}
|
||||||
|
.centerTable {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
table {border: 1px solid black;}
|
||||||
|
tr {text-align: center;}
|
||||||
|
td {text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
|
||||||
|
<h1>Welcome <%= propertiesBean.getName()%></h1>
|
||||||
|
<jsp:include page="header.jsp"></jsp:include>
|
||||||
|
<table class="centerTable">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<% if(propertiesBean.isWorldNewsInterest()) { %>
|
||||||
|
<td><%@include file="worldNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<% if(propertiesBean.isBusinessInterest()) { %>
|
||||||
|
<td><%@include file="businessNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
<td></td>
|
||||||
|
<% if(propertiesBean.isSportsInterest()) { %>
|
||||||
|
<td><%@include file="sportsNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<% if(propertiesBean.isScienceNewsInterest()) { %>
|
||||||
|
<td><%@include file="scienceNews.jsp"%></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td><%@include file="localNews.jsp"%></td>
|
||||||
|
<% } %>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
34
composite-view/web/scienceNews.jsp
Normal file
34
composite-view/web/scienceNews.jsp
Normal file
@ -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" %>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3>
|
||||||
|
Generic Science News
|
||||||
|
</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
New model of gravity proposed for dark matter
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Genetic modifying technique proved on bacteria
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Neurology study maps brain with new precision
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Survey of rainforest discovers 15 new species
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
New signalling pathway for immune system discovered
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
32
composite-view/web/sportsNews.jsp
Normal file
32
composite-view/web/sportsNews.jsp
Normal file
@ -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" %>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h2 { text-align: center;}
|
||||||
|
table {border: 1px solid black;}
|
||||||
|
tr {text-align: center;}
|
||||||
|
td {text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>
|
||||||
|
Generic Sports News
|
||||||
|
</h2>
|
||||||
|
<div style="margin-left: auto; margin-right: auto; padding: 20px">
|
||||||
|
International football match delayed due to weather, will be held next week
|
||||||
|
</div>
|
||||||
|
<div style="margin-left: auto; margin-right: auto; padding: 20px">
|
||||||
|
New rising stars in winter sports, ten new athletes that will shake up the scene
|
||||||
|
</div>
|
||||||
|
<div style="margin-left: auto; margin-right: auto; padding: 20px">
|
||||||
|
Biggest upset in basketball history, upstart team sweeps competition
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
34
composite-view/web/worldNews.jsp
Normal file
34
composite-view/web/worldNews.jsp
Normal file
@ -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" %>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
h2 { text-align: center;}
|
||||||
|
table {border: 1px solid black;}
|
||||||
|
tr {text-align: center;}
|
||||||
|
td {text-align: center;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>
|
||||||
|
Generic World News
|
||||||
|
</h2>
|
||||||
|
<table style="margin-right: auto; margin-left: auto">
|
||||||
|
<tr>
|
||||||
|
<td>New trade talks happening at UN on Thursday</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>European Union to announce new resolution next week</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>UN delivers report on world economic status</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
pom.xml
1
pom.xml
@ -228,6 +228,7 @@
|
|||||||
<module>lockable-object</module>
|
<module>lockable-object</module>
|
||||||
<module>fanout-fanin</module>
|
<module>fanout-fanin</module>
|
||||||
<module>domain-model</module>
|
<module>domain-model</module>
|
||||||
|
<module>composite-view</module>
|
||||||
<module>metadata-mapping</module>
|
<module>metadata-mapping</module>
|
||||||
</modules>
|
</modules>
|
||||||
<repositories>
|
<repositories>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user