diff --git a/app/src/main/java/com/techcourse/DispatcherServlet.java b/app/src/main/java/com/techcourse/DispatcherServlet.java index b0aff7e654..8c60460d1b 100644 --- a/app/src/main/java/com/techcourse/DispatcherServlet.java +++ b/app/src/main/java/com/techcourse/DispatcherServlet.java @@ -1,11 +1,14 @@ package com.techcourse; -import com.interface21.webmvc.servlet.view.JspView; +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.View; +import com.interface21.webmvc.servlet.mvc.tobe.adapter.HandlerAdapter; +import com.interface21.webmvc.servlet.mvc.tobe.adapter.HandlerAdapterRegistry; +import com.interface21.webmvc.servlet.mvc.tobe.mapping.HandlerMappingRegistry; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.util.HashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,32 +17,37 @@ public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); - private ManualHandlerMapping manualHandlerMapping; + private final HandlerMappingRegistry handlerMappingRegistry; + private final HandlerAdapterRegistry handlerAdapterRegistry; - public DispatcherServlet() { + + public DispatcherServlet( + HandlerMappingRegistry handlerMappingRegistry, + HandlerAdapterRegistry handlerAdapterRegistry + ) { + this.handlerMappingRegistry = handlerMappingRegistry; + this.handlerAdapterRegistry = handlerAdapterRegistry; } @Override public void init() { - manualHandlerMapping = new ManualHandlerMapping(); - manualHandlerMapping.initialize(); + handlerMappingRegistry.initialize(); } @Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { - final String requestURI = request.getRequestURI(); - log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI); - + log.debug("Method : {}, Request URI : {}", request.getMethod(), request.getRequestURI()); try { - final var controller = manualHandlerMapping.getHandler(requestURI); - final var viewName = controller.execute(request, response); - final var jspView = new JspView(viewName); - - jspView.render(new HashMap<>(), request, response); - } catch (Throwable e) { - log.error("Exception : {}", e.getMessage(), e); - throw new ServletException(e.getMessage()); + Object handler = handlerMappingRegistry.getHandler(request); + HandlerAdapter handlerAdapter = handlerAdapterRegistry.getHandlerAdapter(handler); + ModelAndView modelAndView = handlerAdapter.handle(request, response, handler); + if (modelAndView != null) { + View view = modelAndView.getView(); + view.render(modelAndView.getModel(), request, response); + } + } catch (Exception e) { + throw new ServletException("요청을 처리하는 것에 실패했습니다", e); } } } diff --git a/app/src/main/java/com/techcourse/DispatcherServletInitializer.java b/app/src/main/java/com/techcourse/DispatcherServletInitializer.java index d07ddf2033..f5b0ffdd33 100644 --- a/app/src/main/java/com/techcourse/DispatcherServletInitializer.java +++ b/app/src/main/java/com/techcourse/DispatcherServletInitializer.java @@ -1,9 +1,14 @@ package com.techcourse; +import com.interface21.web.WebApplicationInitializer; +import com.interface21.webmvc.servlet.mvc.tobe.adapter.HandlerAdapterRegistry; +import com.interface21.webmvc.servlet.mvc.tobe.adapter.ManualHandlerAdapter; +import com.interface21.webmvc.servlet.mvc.tobe.adapter.RequestMappingHandlerAdapter; +import com.interface21.webmvc.servlet.mvc.tobe.mapping.AnnotationHandlerMapping; +import com.interface21.webmvc.servlet.mvc.tobe.mapping.HandlerMappingRegistry; import jakarta.servlet.ServletContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.interface21.web.WebApplicationInitializer; /** * Base class for {@link WebApplicationInitializer} @@ -17,7 +22,7 @@ public class DispatcherServletInitializer implements WebApplicationInitializer { @Override public void onStartup(final ServletContext servletContext) { - final var dispatcherServlet = new DispatcherServlet(); + final var dispatcherServlet = createDispatcherServlet(); final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet); if (registration == null) { @@ -30,4 +35,16 @@ public void onStartup(final ServletContext servletContext) { log.info("Start AppWebApplication Initializer"); } + + private DispatcherServlet createDispatcherServlet() { + HandlerMappingRegistry handlerMappingRegistry = new HandlerMappingRegistry(); + handlerMappingRegistry.addHandlerMapping(new ManualHandlerMapping()); + handlerMappingRegistry.addHandlerMapping(new AnnotationHandlerMapping(Application.class)); + + HandlerAdapterRegistry handlerAdapterRegistry = new HandlerAdapterRegistry(); + handlerAdapterRegistry.addHandlerAdapter(new RequestMappingHandlerAdapter()); + handlerAdapterRegistry.addHandlerAdapter(new ManualHandlerAdapter()); + + return new DispatcherServlet(handlerMappingRegistry, handlerAdapterRegistry); + } } diff --git a/app/src/main/java/com/techcourse/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/ManualHandlerMapping.java index 2116056394..6cf5383abf 100644 --- a/app/src/main/java/com/techcourse/ManualHandlerMapping.java +++ b/app/src/main/java/com/techcourse/ManualHandlerMapping.java @@ -1,34 +1,38 @@ package com.techcourse; -import com.techcourse.controller.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.interface21.webmvc.servlet.mvc.asis.Controller; import com.interface21.webmvc.servlet.mvc.asis.ForwardController; - +import com.interface21.webmvc.servlet.mvc.tobe.mapping.HandlerMapping; +import com.techcourse.controller.LoginController; +import com.techcourse.controller.LoginViewController; +import com.techcourse.controller.LogoutController; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class ManualHandlerMapping { +public class ManualHandlerMapping implements HandlerMapping { private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); private static final Map controllers = new HashMap<>(); + @Override public void initialize() { controllers.put("/", new ForwardController("/index.jsp")); controllers.put("/login", new LoginController()); controllers.put("/login/view", new LoginViewController()); controllers.put("/logout", new LogoutController()); - controllers.put("/register/view", new RegisterViewController()); - controllers.put("/register", new RegisterController()); log.info("Initialized Handler Mapping!"); controllers.keySet() .forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass())); } - public Controller getHandler(final String requestURI) { + @Override + public Controller getHandler(final HttpServletRequest request) { + String requestURI = request.getRequestURI(); log.debug("Request Mapping Uri : {}", requestURI); return controllers.get(requestURI); } diff --git a/app/src/main/java/com/techcourse/controller/RegisterController.java b/app/src/main/java/com/techcourse/controller/RegisterController.java index 782abfb219..26029f809e 100644 --- a/app/src/main/java/com/techcourse/controller/RegisterController.java +++ b/app/src/main/java/com/techcourse/controller/RegisterController.java @@ -1,21 +1,35 @@ package com.techcourse.controller; +import com.interface21.web.bind.annotation.RequestMapping; +import com.interface21.web.bind.annotation.RequestMethod; +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.View; +import com.interface21.webmvc.servlet.view.JspView; import com.techcourse.domain.User; import com.techcourse.repository.InMemoryUserRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import com.interface21.webmvc.servlet.mvc.asis.Controller; -public class RegisterController implements Controller { +@com.interface21.context.stereotype.Controller +public class RegisterController { - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + private static final String REGISTER_JSP = "/register.jsp"; + + @RequestMapping(value = "/register", method = RequestMethod.GET) + public ModelAndView registerPage(final HttpServletRequest req, final HttpServletResponse res) { + JspView jspView = new JspView(REGISTER_JSP); + return new ModelAndView(jspView); + } + + @RequestMapping(value = "/register", method = RequestMethod.POST) + public ModelAndView registerUser(final HttpServletRequest req, final HttpServletResponse res) { final var user = new User(2, req.getParameter("account"), req.getParameter("password"), req.getParameter("email")); InMemoryUserRepository.save(user); - return "redirect:/index.jsp"; + View view = new JspView("redirect:/index.jsp"); + return new ModelAndView(view); } } diff --git a/app/src/main/java/com/techcourse/controller/RegisterViewController.java b/app/src/main/java/com/techcourse/controller/RegisterViewController.java deleted file mode 100644 index c88dc2814b..0000000000 --- a/app/src/main/java/com/techcourse/controller/RegisterViewController.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.techcourse.controller; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import com.interface21.webmvc.servlet.mvc.asis.Controller; - -public class RegisterViewController implements Controller { - - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - return "/register.jsp"; - } -} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScanner.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScanner.java index be311f3970..3e0406d53f 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScanner.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScanner.java @@ -2,25 +2,28 @@ import com.interface21.context.stereotype.Controller; import com.interface21.webmvc.servlet.exception.ControllerScanException; +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import org.reflections.Reflections; public class ControllerScanner { + private static final Class CONTROLLER_ANNOTATION = Controller.class; + private final Object[] basePackage; - private final Map, Object> controllers = new ConcurrentHashMap(); + private final Map, Object> controllers = new HashMap<>(); public ControllerScanner(Object... basePackage) { this.basePackage = basePackage; - scanControllers(); + scanController(); } - private void scanControllers() { + private void scanController() { Reflections reflections = new Reflections(basePackage); - Set> annotatedControllerTypes = reflections.getTypesAnnotatedWith(Controller.class); - annotatedControllerTypes.forEach(this::addController); + reflections.getTypesAnnotatedWith(CONTROLLER_ANNOTATION) + .forEach(this::addController); } private void addController(Class annotatedControllerType) { @@ -33,6 +36,6 @@ private void addController(Class annotatedControllerType) { } public Map, Object> getControllers() { - return controllers; + return Collections.unmodifiableMap(controllers); } } diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapter.java new file mode 100644 index 0000000000..73143b1a8e --- /dev/null +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapter.java @@ -0,0 +1,12 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import com.interface21.webmvc.servlet.ModelAndView; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public interface HandlerAdapter { + + boolean supports(Object handler); + + ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; +} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapterRegistry.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapterRegistry.java new file mode 100644 index 0000000000..8e023c6381 --- /dev/null +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapterRegistry.java @@ -0,0 +1,25 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + +public class HandlerAdapterRegistry { + + private final List handlerAdapters = new ArrayList<>(); + + public HandlerAdapterRegistry() { + } + + public void addHandlerAdapter(HandlerAdapter handlerAdapter) { + handlerAdapters.add(handlerAdapter); + } + + public HandlerAdapter getHandlerAdapter(Object handler) { + return handlerAdapters.stream() + .filter(handlerAdapter -> handlerAdapter.supports(handler)) + .findFirst() + .orElseThrow(() -> new NoSuchElementException("핸들러를 처리할 수 있는 Adapter를 찾지 못했습니다")); + } +} + diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/ManualHandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/ManualHandlerAdapter.java new file mode 100644 index 0000000000..35219b7a6c --- /dev/null +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/ManualHandlerAdapter.java @@ -0,0 +1,23 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.mvc.asis.Controller; +import com.interface21.webmvc.servlet.view.JspView; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class ManualHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(Object handler) { + return handler instanceof Controller; + } + + @Override + public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + String viewName = ((Controller) handler).execute(request, response); + JspView jspView = new JspView(viewName); + return new ModelAndView(jspView); + } +} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/RequestMappingHandlerAdapter.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/RequestMappingHandlerAdapter.java new file mode 100644 index 0000000000..4e9158e906 --- /dev/null +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/RequestMappingHandlerAdapter.java @@ -0,0 +1,21 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecution; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class RequestMappingHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(Object handler) { + return handler instanceof HandlerExecution; + } + + @Override + public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + HandlerExecution handlerExecution = (HandlerExecution) handler; + return handlerExecution.handle(request, response); + } +} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMapping.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/AnnotationHandlerMapping.java similarity index 85% rename from mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMapping.java rename to mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/AnnotationHandlerMapping.java index 275716bbe4..e8a7d9cb48 100644 --- a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/AnnotationHandlerMapping.java @@ -1,8 +1,11 @@ -package com.interface21.webmvc.servlet.mvc.tobe; +package com.interface21.webmvc.servlet.mvc.tobe.mapping; import com.interface21.web.bind.annotation.RequestMapping; import com.interface21.web.bind.annotation.RequestMethod; import com.interface21.webmvc.servlet.exception.HandlerInitializationException; +import com.interface21.webmvc.servlet.mvc.tobe.ControllerScanner; +import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecution; +import com.interface21.webmvc.servlet.mvc.tobe.HandlerKey; import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Arrays; @@ -12,19 +15,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AnnotationHandlerMapping { +public class AnnotationHandlerMapping implements HandlerMapping { private static final Logger log = LoggerFactory.getLogger(AnnotationHandlerMapping.class); private final Map handlerExecutions; private final ControllerScanner controllerScanner; - public AnnotationHandlerMapping(ControllerScanner controllerScanner) { + public AnnotationHandlerMapping(Object... basePackage) { this.handlerExecutions = new HashMap<>(); - this.controllerScanner = controllerScanner; + this.controllerScanner = new ControllerScanner(basePackage); } + @Override + public void initialize() { + Map, Object> controllers = controllerScanner.getControllers(); + controllers.forEach(this::addHandlers); + log.info("Initialized AnnotationHandlerMapping!"); + } + @Override public Object getHandler(final HttpServletRequest request) { String requestURI = request.getRequestURI(); RequestMethod method = RequestMethod.valueOf(request.getMethod()); @@ -33,12 +43,6 @@ public Object getHandler(final HttpServletRequest request) { return handlerExecutions.get(handlerKey); } - public void initialize() { - Map, Object> controllers = controllerScanner.getControllers(); - controllers.forEach(this::addHandlers); - log.info("Initialized AnnotationHandlerMapping!"); - } - private void addHandlers(final Class controllerType, final Object instance) { try { Arrays.stream(controllerType.getMethods()) @@ -56,7 +60,7 @@ private void addHandler(final Method method, final Object controllerInstance) { private List createHandlerKeys(final Method method) { RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); - + return Arrays.stream(getMappingMethods(requestMapping)) .map(requestMethod -> new HandlerKey(requestMapping.value(), requestMethod)) .toList(); diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMapping.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMapping.java new file mode 100644 index 0000000000..017da7e674 --- /dev/null +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMapping.java @@ -0,0 +1,10 @@ +package com.interface21.webmvc.servlet.mvc.tobe.mapping; + +import jakarta.servlet.http.HttpServletRequest; + +public interface HandlerMapping { + + void initialize(); + + Object getHandler(HttpServletRequest request); +} diff --git a/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMappingRegistry.java b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMappingRegistry.java new file mode 100644 index 0000000000..0641fc0fb9 --- /dev/null +++ b/mvc/src/main/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMappingRegistry.java @@ -0,0 +1,31 @@ +package com.interface21.webmvc.servlet.mvc.tobe.mapping; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; + +public class HandlerMappingRegistry { + + private final List handlerMappings = new ArrayList<>(); + + public HandlerMappingRegistry() { + } + + public void initialize() { + handlerMappings.forEach(HandlerMapping::initialize); + } + + public void addHandlerMapping(HandlerMapping handlerMapping) { + handlerMappings.add(handlerMapping); + } + + public Object getHandler(HttpServletRequest httpServletRequest) { + return handlerMappings.stream() + .map(hm -> hm.getHandler(httpServletRequest)) + .filter(Objects::nonNull) + .findFirst() + .orElseThrow(() -> new NoSuchElementException("요청을 처리할 수 있는 핸들러 매핑 정보가 없습니다")); + } +} diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java index 394c5e2fde..e3ce1efc7e 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/AnnotationHandlerMappingTest.java @@ -4,18 +4,21 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.interface21.webmvc.servlet.mvc.tobe.mapping.AnnotationHandlerMapping; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +@DisplayName("AnnotationHandlerMapping 테스트") class AnnotationHandlerMappingTest { private AnnotationHandlerMapping handlerMapping; @BeforeEach void setUp() { - handlerMapping = new AnnotationHandlerMapping(new ControllerScanner("samples")); + handlerMapping = new AnnotationHandlerMapping("samples"); handlerMapping.initialize(); } diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScannerTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScannerTest.java index 78798e6ed4..e8edaf71e0 100644 --- a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScannerTest.java +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/ControllerScannerTest.java @@ -1,6 +1,7 @@ package com.interface21.webmvc.servlet.mvc.tobe; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import com.interface21.context.stereotype.Controller; import java.util.Map; @@ -31,6 +32,14 @@ void testScanControllers() { ); } + @DisplayName("컨트롤러스캐너에서 반환한 컨트롤러 정보를 변경하려고 한다면 예외가 발생한다") + @Test + void controllersReturnedAsUnmodifiableMap() { + Map, Object> controllers = controllerScanner.getControllers(); + assertThatThrownBy(() -> controllers.put(Object.class, new Object())) + .isInstanceOf(UnsupportedOperationException.class); + } + @Controller private static class SampleController { diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapterRegistryTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapterRegistryTest.java new file mode 100644 index 0000000000..ad79d37900 --- /dev/null +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/HandlerAdapterRegistryTest.java @@ -0,0 +1,56 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("HandlerAdapterRegistry 테스트") +class HandlerAdapterRegistryTest { + + private HandlerAdapterRegistry handlerAdapterRegistry; + private HandlerAdapter mockAdapter1; + private HandlerAdapter mockAdapter2; + + @BeforeEach + void setUp() { + handlerAdapterRegistry = new HandlerAdapterRegistry(); + mockAdapter1 = mock(HandlerAdapter.class); + mockAdapter2 = mock(HandlerAdapter.class); + } + + @Test + @DisplayName("지원하는 Adapter 반환 테스트") + void returnCorrectAdapter() { + Object handler = new Object(); + when(mockAdapter1.supports(handler)).thenReturn(false); + when(mockAdapter2.supports(handler)).thenReturn(true); + + handlerAdapterRegistry.addHandlerAdapter(mockAdapter1); + handlerAdapterRegistry.addHandlerAdapter(mockAdapter2); + + HandlerAdapter result = handlerAdapterRegistry.getHandlerAdapter(handler); + assertThat(result).isEqualTo(mockAdapter2); + } + + @Test + @DisplayName("지원하는 Adapter가 없을 때 예외 발생 테스트") + void throwExceptionIfNoAdapter() { + Object handler = new Object(); + + when(mockAdapter1.supports(handler)).thenReturn(false); + when(mockAdapter2.supports(handler)).thenReturn(false); + + handlerAdapterRegistry.addHandlerAdapter(mockAdapter1); + handlerAdapterRegistry.addHandlerAdapter(mockAdapter2); + + assertThatThrownBy(() -> handlerAdapterRegistry.getHandlerAdapter(handler)) + .isInstanceOf(NoSuchElementException.class) + .hasMessageContaining("핸들러를 처리할 수 있는 Adapter를 찾지 못했습니다"); + } +} diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/ManualHandlerAdapterTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/ManualHandlerAdapterTest.java new file mode 100644 index 0000000000..3b204ec828 --- /dev/null +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/ManualHandlerAdapterTest.java @@ -0,0 +1,50 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.interface21.webmvc.servlet.ModelAndView; +import com.interface21.webmvc.servlet.mvc.asis.Controller; +import com.interface21.webmvc.servlet.view.JspView; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("ManualHandlerAdapter 테스트") +class ManualHandlerAdapterTest { + + private ManualHandlerAdapter manualHandlerAdapter; + private Controller mockController; + private HttpServletRequest mockRequest; + private HttpServletResponse mockResponse; + + @BeforeEach + void setUp() { + manualHandlerAdapter = new ManualHandlerAdapter(); + mockController = mock(Controller.class); + mockRequest = mock(HttpServletRequest.class); + mockResponse = mock(HttpServletResponse.class); + } + + @Test + @DisplayName("Handler 지원 여부 확인") + void supports() { + Assertions.assertAll( + () -> assertThat(manualHandlerAdapter.supports(mockController)).isTrue(), + () -> assertThat(manualHandlerAdapter.supports(new Object())).isFalse() + ); + } + + @Test + @DisplayName("Handler 처리 및 ModelAndView 반환") + void handle() throws Exception { + when(mockController.execute(mockRequest, mockResponse)).thenReturn("/test.jsp"); + + ModelAndView modelAndView = manualHandlerAdapter.handle(mockRequest, mockResponse, mockController); + assertThat(modelAndView.getView()).isInstanceOf(JspView.class); + } +} diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/RequestMappingHandlerAdapterTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/RequestMappingHandlerAdapterTest.java new file mode 100644 index 0000000000..b88986221a --- /dev/null +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/adapter/RequestMappingHandlerAdapterTest.java @@ -0,0 +1,46 @@ +package com.interface21.webmvc.servlet.mvc.tobe.adapter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + +import com.interface21.webmvc.servlet.mvc.tobe.HandlerExecution; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("RequestMappingHandlerAdapter 테스트") +class RequestMappingHandlerAdapterTest { + + private RequestMappingHandlerAdapter handlerAdapter; + private HandlerExecution mockHandlerExecution; + private HttpServletRequest mockRequest; + private HttpServletResponse mockResponse; + + @BeforeEach + void setUp() { + handlerAdapter = new RequestMappingHandlerAdapter(); + mockHandlerExecution = mock(HandlerExecution.class); + mockRequest = mock(HttpServletRequest.class); + mockResponse = mock(HttpServletResponse.class); + } + + @Test + @DisplayName("HandlerExecution 지원 여부 테스트") + void supportsHandlerExecution() { + Assertions.assertAll( + () -> assertThat(handlerAdapter.supports(mockHandlerExecution)).isTrue(), + () -> assertThat(handlerAdapter.supports(new Object())).isFalse() + ); + } + + @Test + @DisplayName("잘못된 핸들러 타입 처리 시 예외 발생 테스트") + void throwExceptionForInvalidHandler() { + assertThatThrownBy(() -> handlerAdapter.handle(mockRequest, mockResponse, new Object())) + .isInstanceOf(ClassCastException.class); + } +} diff --git a/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMappingRegistryTest.java b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMappingRegistryTest.java new file mode 100644 index 0000000000..7355fe37d0 --- /dev/null +++ b/mvc/src/test/java/com/interface21/webmvc/servlet/mvc/tobe/mapping/HandlerMappingRegistryTest.java @@ -0,0 +1,57 @@ +package com.interface21.webmvc.servlet.mvc.tobe.mapping; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("HandlerMappingRegistry 테스트") +class HandlerMappingRegistryTest { + + private HandlerMappingRegistry handlerMappingRegistry; + private HandlerMapping mockHandlerMapping1; + private HandlerMapping mockHandlerMapping2; + private HttpServletRequest mockRequest; + + @BeforeEach + void setUp() { + handlerMappingRegistry = new HandlerMappingRegistry(); + mockHandlerMapping1 = mock(HandlerMapping.class); + mockHandlerMapping2 = mock(HandlerMapping.class); + mockRequest = mock(HttpServletRequest.class); + } + + @Test + @DisplayName("지원하는 핸들러 반환 테스트") + void returnCorrectHandler() { + Object expectedHandler = new Object(); + when(mockHandlerMapping1.getHandler(mockRequest)).thenReturn(null); + when(mockHandlerMapping2.getHandler(mockRequest)).thenReturn(expectedHandler); + + handlerMappingRegistry.addHandlerMapping(mockHandlerMapping1); + handlerMappingRegistry.addHandlerMapping(mockHandlerMapping2); + + Object result = handlerMappingRegistry.getHandler(mockRequest); + assertThat(result).isEqualTo(expectedHandler); + } + + @Test + @DisplayName("지원하는 핸들러가 없을 때 예외 발생 테스트") + void throwExceptionIfNoHandler() { + when(mockHandlerMapping1.getHandler(mockRequest)).thenReturn(null); + when(mockHandlerMapping2.getHandler(mockRequest)).thenReturn(null); + + handlerMappingRegistry.addHandlerMapping(mockHandlerMapping1); + handlerMappingRegistry.addHandlerMapping(mockHandlerMapping2); + + assertThatThrownBy(() -> handlerMappingRegistry.getHandler(mockRequest)) + .isInstanceOf(NoSuchElementException.class) + .hasMessageContaining("요청을 처리할 수 있는 핸들러 매핑 정보가 없습니다"); + } +}