Skip to content

Commit

Permalink
[MVC 구현하기 - 1단계] 레오(차영호) 미션 제출합니다. (#387)
Browse files Browse the repository at this point in the history
* 패키지 위치 변경 및 코드 정리

* 서블릿 학습 테스트 코드 개선

* test: 학습 테스트

* feat: mvc 구현하기

* feat: RequestMethod from 정팩메 작성

---------

Co-authored-by: kang-hyungu <[email protected]>
  • Loading branch information
youngh0 and kang-hyungu authored Sep 14, 2023
1 parent b16924a commit 3a3e4b0
Show file tree
Hide file tree
Showing 50 changed files with 472 additions and 236 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: ./gradlew build sonar --info -x :study:build
run: ./gradlew clean build codeCoverageReport --info -x :study:build
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,5 @@ Temporary Items

tomcat.*
tomcat.*/**

**/WEB-INF/classes/**
24 changes: 0 additions & 24 deletions app/src/main/java/com/techcourse/AppWebApplicationInitializer.java

This file was deleted.

33 changes: 16 additions & 17 deletions app/src/main/java/com/techcourse/Application.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.techcourse;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.stream.Stream;

public class Application {
Expand All @@ -16,22 +14,11 @@ public class Application {

public static void main(final String[] args) throws Exception {
final int port = defaultPortIfNull(args);

final var tomcat = new Tomcat();
tomcat.setConnector(createConnector(port));
final var docBase = new File("app/src/main/webapp/").getAbsolutePath();
tomcat.addWebapp("", docBase);
log.info("configuring app with basedir: {}", docBase);
final var tomcat = new TomcatStarter(port);
log.info("configuring app with basedir: {}", TomcatStarter.WEBAPP_DIR_LOCATION);

tomcat.start();
tomcat.getServer().await();
}

private static Connector createConnector(final int port) {
final var connector = new Connector();
connector.setPort(port);
connector.setProperty("bindOnInit", "false");
return connector;
stop(tomcat);
}

private static int defaultPortIfNull(final String[] args) {
Expand All @@ -40,4 +27,16 @@ private static int defaultPortIfNull(final String[] args) {
.map(Integer::parseInt)
.orElse(DEFAULT_PORT);
}

private static void stop(final TomcatStarter tomcat) {
try {
// make the application wait until we press any key.
System.in.read();
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
log.info("web server stop.");
tomcat.stop();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
package nextstep.mvc;
package com.techcourse;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import nextstep.mvc.controller.asis.Controller;
import nextstep.mvc.view.JspView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.view.JspView;

public class DispatcherServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);

private HandlerMapping handlerMappings;
private ManualHandlerMapping manualHandlerMapping;

public void addHandlerMapping(final HandlerMapping handlerMapping) {
this.handlerMappings = handlerMapping;
public DispatcherServlet() {
}

@Override
public void init() {
this.handlerMappings.initialize();
manualHandlerMapping = new ManualHandlerMapping();
manualHandlerMapping.initialize();
}

@Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
log.debug("Method : {}, Request URI : {}", request.getMethod(), request.getRequestURI());
final String requestURI = request.getRequestURI();
log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI);

try {
final var controller = (Controller) handlerMappings.getHandler(request);
final var controller = manualHandlerMapping.getHandler(requestURI);
final var viewName = controller.execute(request, response);
move(viewName, request, response);
} catch (Throwable e) {
Expand Down
33 changes: 33 additions & 0 deletions app/src/main/java/com/techcourse/DispatcherServletInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.techcourse;

import jakarta.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.WebApplicationInitializer;

/**
* Base class for {@link WebApplicationInitializer}
* implementations that register a {@link DispatcherServlet} in the servlet context.
*/
public class DispatcherServletInitializer implements WebApplicationInitializer {

private static final Logger log = LoggerFactory.getLogger(DispatcherServletInitializer.class);

private static final String DEFAULT_SERVLET_NAME = "dispatcher";

@Override
public void onStartup(final ServletContext servletContext) {
final var dispatcherServlet = new DispatcherServlet();

final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " +
"Check if there is another servlet registered under the same name.");
}

registration.setLoadOnStartup(1);
registration.addMapping("/");

log.info("Start AppWebApplication Initializer");
}
}
13 changes: 4 additions & 9 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
package com.techcourse;

import com.techcourse.controller.*;
import jakarta.servlet.http.HttpServletRequest;
import nextstep.mvc.HandlerMapping;
import nextstep.mvc.controller.asis.Controller;
import nextstep.mvc.controller.asis.ForwardController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.asis.ForwardController;

import java.util.HashMap;
import java.util.Map;

public class ManualHandlerMapping implements HandlerMapping {
public class ManualHandlerMapping {

private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class);

private static final Map<String, Controller> controllers = new HashMap<>();

@Override
public void initialize() {
controllers.put("/", new ForwardController("/index.jsp"));
controllers.put("/login", new LoginController());
Expand All @@ -31,9 +28,7 @@ public void initialize() {
.forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass()));
}

@Override
public Controller getHandler(HttpServletRequest request) {
final String requestURI = request.getRequestURI();
public Controller getHandler(final String requestURI) {
log.debug("Request Mapping Uri : {}", requestURI);
return controllers.get(requestURI);
}
Expand Down
83 changes: 83 additions & 0 deletions app/src/main/java/com/techcourse/TomcatStarter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.techcourse;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.scan.StandardJarScanner;

import java.io.File;

public class TomcatStarter {

public static final String WEBAPP_DIR_LOCATION = "app/src/main/webapp/";

private final Tomcat tomcat;

public TomcatStarter(final int port) {
this(WEBAPP_DIR_LOCATION, port);
}

public TomcatStarter(final String webappDirLocation, final int port) {
this.tomcat = new Tomcat();
tomcat.setConnector(createConnector(port));

final var docBase = new File(webappDirLocation).getAbsolutePath();
final var context = (StandardContext) tomcat.addWebapp("", docBase);
skipJarScan(context);
skipClearReferences(context);
}

public void start() {
try {
tomcat.start();
} catch (LifecycleException e) {
throw new UncheckedServletException(e);
}
}

public void stop() {
try {
tomcat.stop();
tomcat.destroy();
} catch (LifecycleException e) {
throw new UncheckedServletException(e);
}
}

private Connector createConnector(final int port) {
final var connector = new Connector();
connector.setPort(port);
return connector;
}

private void skipJarScan(final Context context) {
final var jarScanner = (StandardJarScanner) context.getJarScanner();
jarScanner.setScanClassPath(false);
}

private void skipClearReferences(final StandardContext context) {
/**
* https://tomcat.apache.org/tomcat-10.1-doc/config/context.html
*
* setClearReferencesObjectStreamClassCaches 번역
* true인 경우 웹 응용 프로그램이 중지되면 Tomcat은 직렬화에 사용되는
* ObjectStreamClass 클래스에서 웹 응용 프로그램에 의해 로드된
* 클래스에 대한 SoftReference를 찾고 찾은 모든 SoftReference를 지웁니다.
* 이 기능은 리플렉션을 사용하여 SoftReference를 식별하므로 Java 9 이상에서
* 실행할 때 명령줄 옵션 -XaddExports:java.base/java.io=ALL-UNNAMED를 설정해야 합니다.
* 지정하지 않으면 기본값인 true가 사용됩니다.
*
* ObjectStreamClass와 관련된 메모리 누수는 Java 19 이상, Java 17.0.4 이상 및
* Java 11.0.16 이상에서 수정되었습니다.
* 수정 사항이 포함된 Java 버전에서 실행할 때 확인이 비활성화됩니다.
*
* Amazon Corretto-17.0.6은 경고 메시지가 나옴.
* 학습과 관련 없는 메시지가 나오지 않도록 관련 설정을 끈다.
*/
context.setClearReferencesObjectStreamClassCaches(false);
context.setClearReferencesRmiTargets(false);
context.setClearReferencesThreadLocals(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.techcourse;

public class UncheckedServletException extends RuntimeException {

public UncheckedServletException(Exception e) {
super(e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.techcourse.repository.InMemoryUserRepository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import nextstep.mvc.controller.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -31,8 +31,7 @@ private String login(final HttpServletRequest request, final User user) {
final var session = request.getSession();
session.setAttribute(UserSession.SESSION_KEY, user);
return "redirect:/index.jsp";
} else {
return "redirect:/401.jsp";
}
return "redirect:/401.jsp";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import nextstep.mvc.controller.asis.Controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;

public class LoginViewController implements Controller {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import nextstep.mvc.controller.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;

public class LogoutController implements Controller {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.techcourse.repository.InMemoryUserRepository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import nextstep.mvc.controller.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;

public class RegisterController implements Controller {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import nextstep.mvc.controller.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;

public class RegisterViewController implements Controller {

Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ plugins {
id "org.sonarqube" version "4.2.1.3168"
}

allprojects {
repositories {
mavenCentral()
}
}

subprojects {
apply plugin: 'org.sonarqube'
sonar {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nextstep.web.annotation;
package context.org.springframework.stereotype;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand Down
Loading

0 comments on commit 3a3e4b0

Please sign in to comment.