Skip to content

Commit 59913f7

Browse files
committed
GP-82 add legacy hello-spring-mvc exercise
1 parent bf8d812 commit 59913f7

File tree

9 files changed

+287
-0
lines changed

9 files changed

+287
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Hello Spring MVC exercise :muscle:
2+
Improve your *Spring MVC* configuration skills
3+
### Task
4+
The task is to **configure a typical *Spring MVC* application** separating web and root configs. Your job is to follow
5+
the instructions in the *todo* section and **implement a proper configuration.**
6+
7+
To verify your configuration, run `WelcomeWebAppTest.java`
8+
9+
10+
### Pre-conditions :heavy_exclamation_mark:
11+
You're supposed to be familiar with *Spring MVC*
12+
13+
### How to start :question:
14+
* Just clone the repository and start implementing the **todo** section, verify your changes by running tests
15+
* If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
16+
* Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
17+
18+
### Related materials :information_source:
19+
* [Spring MVC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-mvc-basics)<img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=20/>
20+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>3-0-spring-framework</artifactId>
7+
<groupId>com.bobocode</groupId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>3-1-1-dispatcher-servlet-initializer</artifactId>
13+
<packaging>war</packaging>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>org.springframework</groupId>
18+
<artifactId>spring-webmvc</artifactId>
19+
<version>5.0.7.RELEASE</version>
20+
</dependency>
21+
<dependency>
22+
<groupId>javax.servlet</groupId>
23+
<artifactId>javax.servlet-api</artifactId>
24+
<version>4.0.1</version>
25+
<scope>provided</scope>
26+
</dependency>
27+
</dependencies>
28+
29+
<build>
30+
<plugins>
31+
32+
<plugin>
33+
<groupId>org.apache.maven.plugins</groupId>
34+
<artifactId>maven-war-plugin</artifactId>
35+
<version>2.6</version>
36+
<configuration>
37+
<failOnMissingWebXml>false</failOnMissingWebXml>
38+
</configuration>
39+
</plugin>
40+
41+
</plugins>
42+
</build>
43+
</project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.bobocode.config;
2+
3+
import org.springframework.stereotype.Controller;
4+
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
5+
6+
/**
7+
* This class provides application root (non-web) configuration.
8+
* <p>
9+
* todo: mark this class as config
10+
* todo: enable component scanning for all packages in "com.bobocode"
11+
* todo: ignore all web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc}) using exclude filter
12+
*/
13+
public class RootConfig {
14+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.bobocode.config;
2+
3+
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
4+
5+
/**
6+
* This class is used to configure DispatcherServlet and links it with application config classes
7+
* <p>
8+
* todo: provide default servlet mapping ("/")
9+
* todo: use {@link WebConfig} as ServletConfig class
10+
* todo: use {@link RootConfig} as root application config class
11+
*/
12+
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
13+
@Override
14+
protected Class<?>[] getRootConfigClasses() {
15+
throw new UnsupportedOperationException("Method is not implemented yet!");
16+
}
17+
18+
@Override
19+
protected Class<?>[] getServletConfigClasses() {
20+
throw new UnsupportedOperationException("Method is not implemented yet!");
21+
}
22+
23+
@Override
24+
protected String[] getServletMappings() {
25+
throw new UnsupportedOperationException("Method is not implemented yet!");
26+
}
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.bobocode.config;
2+
3+
/**
4+
* This class provides web (servlet) related configuration.
5+
* <p>
6+
* todo: mark this class as Spring config class
7+
* todo: enable web mvc using annotation
8+
* todo: enable component scanning for package "web"
9+
*/
10+
public class WebConfig {
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.bobocode.web.controller;
2+
3+
/**
4+
* Welcome controller that consists of one method that handles get request to "/welcome" and respond with a message.
5+
* <p>
6+
* todo: mark this class as Spring controller
7+
* todo: configure HTTP GET mapping "/welcome" for method {@link WelcomeController#welcome()}
8+
* todo: tell Spring that {@link WelcomeController#welcome()} method provides response body
9+
*/
10+
public class WelcomeController {
11+
12+
public String welcome() {
13+
return "Welcome to Spring MVC!";
14+
}
15+
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.bobocode;
2+
3+
import com.bobocode.config.WebAppInitializer;
4+
5+
class WebAppInitializerWrapper extends WebAppInitializer {
6+
public String[] getServletMappings() {
7+
return super.getServletMappings();
8+
}
9+
10+
public Class[] getRootConfigClasses() {
11+
return super.getRootConfigClasses();
12+
}
13+
14+
public Class<?>[] getServletConfigClasses() {
15+
return super.getServletConfigClasses();
16+
}
17+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package com.bobocode;
2+
3+
import com.bobocode.config.RootConfig;
4+
import com.bobocode.config.WebConfig;
5+
import com.bobocode.web.controller.WelcomeController;
6+
import org.junit.jupiter.api.Test;
7+
import org.springframework.context.annotation.ComponentScan;
8+
import org.springframework.context.annotation.ComponentScan.Filter;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.context.annotation.FilterType;
11+
import org.springframework.stereotype.Controller;
12+
import org.springframework.web.bind.annotation.GetMapping;
13+
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
14+
15+
import java.util.List;
16+
import java.util.stream.Collectors;
17+
import java.util.stream.Stream;
18+
19+
import static org.hamcrest.MatcherAssert.assertThat;
20+
import static org.hamcrest.Matchers.arrayContaining;
21+
import static org.hamcrest.Matchers.arrayWithSize;
22+
import static org.hamcrest.Matchers.containsInAnyOrder;
23+
import static org.hamcrest.Matchers.equalTo;
24+
import static org.hamcrest.Matchers.notNullValue;
25+
26+
class WelcomeWebAppTest {
27+
28+
@Test
29+
void testDispatcherServletMapping() {
30+
WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
31+
32+
assertThat(webAppInitializerWrapper.getServletMappings(), arrayContaining("/"));
33+
}
34+
35+
@Test
36+
void testInitializerRootConfigClasses() {
37+
WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
38+
39+
assertThat(webAppInitializerWrapper.getRootConfigClasses(), arrayContaining(RootConfig.class));
40+
}
41+
42+
@Test
43+
void testInitializerWebConfigClasses() {
44+
WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
45+
46+
assertThat(webAppInitializerWrapper.getServletConfigClasses(), arrayContaining(WebConfig.class));
47+
}
48+
49+
@Test
50+
void testRootConfigClassIsMarkedAsConfiguration() {
51+
Configuration configuration = RootConfig.class.getAnnotation(Configuration.class);
52+
53+
assertThat(configuration, notNullValue());
54+
}
55+
56+
@Test
57+
void testRootConfigClassEnablesComponentScan() {
58+
ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
59+
60+
assertThat(componentScan, notNullValue());
61+
}
62+
63+
@Test
64+
void testRootConfigComponentScanPackages() {
65+
ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
66+
String[] packages = componentScan.basePackages();
67+
if (packages.length == 0) {
68+
packages = componentScan.value();
69+
}
70+
71+
assertThat(packages, arrayContaining("com.bobocode"));
72+
}
73+
74+
@Test
75+
void testRootConfigComponentScanFilters() {
76+
ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
77+
Filter[] filters = componentScan.excludeFilters();
78+
List<Class> filteredClasses = getFilteredClasses(filters);
79+
80+
assertThat(filters, arrayWithSize(2));
81+
assertThat(filters[0].type(), equalTo(FilterType.ANNOTATION));
82+
assertThat(filters[1].type(), equalTo(FilterType.ANNOTATION));
83+
assertThat(filteredClasses, containsInAnyOrder(EnableWebMvc.class, Controller.class));
84+
}
85+
86+
private List<Class> getFilteredClasses(Filter[] filters) {
87+
return Stream.of(filters).flatMap(filter -> Stream.of(filter.value())).collect(Collectors.toList());
88+
}
89+
90+
@Test
91+
void testWebConfigIsMarkedAsConfiguration() {
92+
Configuration configuration = WebConfig.class.getAnnotation(Configuration.class);
93+
94+
assertThat(configuration, notNullValue());
95+
}
96+
97+
@Test
98+
void testWebConfigEnablesComponentScan() {
99+
ComponentScan componentScan = WebConfig.class.getAnnotation(ComponentScan.class);
100+
101+
assertThat(componentScan, notNullValue());
102+
}
103+
104+
@Test
105+
void testWebConfigComponentScanPackages() {
106+
ComponentScan componentScan = WebConfig.class.getAnnotation(ComponentScan.class);
107+
108+
assertThat(componentScan.basePackages(), arrayContaining("com.bobocode.web"));
109+
}
110+
111+
@Test
112+
void testWebConfigEnablesWebMvc() {
113+
EnableWebMvc enableWebMvc = WebConfig.class.getAnnotation(EnableWebMvc.class);
114+
115+
assertThat(enableWebMvc, notNullValue());
116+
}
117+
118+
@Test
119+
void testWelcomeControllerIsMarkedAsController() {
120+
Controller controller = WelcomeController.class.getAnnotation(Controller.class);
121+
122+
assertThat(controller, notNullValue());
123+
}
124+
125+
@Test
126+
void testWelcomeControllerMethodIsMarkedAsGetMethod() throws NoSuchMethodException {
127+
GetMapping getMapping = WelcomeController.class.getDeclaredMethod("welcome").getAnnotation(GetMapping.class);
128+
129+
assertThat(getMapping, notNullValue());
130+
}
131+
132+
@Test
133+
void testWelcomeControllerMethodMapping() throws NoSuchMethodException {
134+
GetMapping getMapping = WelcomeController.class.getDeclaredMethod("welcome").getAnnotation(GetMapping.class);
135+
136+
assertThat(getMapping.value(), arrayContaining("/welcome"));
137+
}
138+
}

3-0-spring-framework/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<modules>
1616
<module>3-0-1-hello-spring-mvc</module>
17+
<module>3-1-1-dispatcher-servlet-initializer</module>
1718
</modules>
1819

1920
</project>

0 commit comments

Comments
 (0)