diff --git a/app/build.gradle b/app/build.gradle index b96dc81587..7ba3a68bd9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,7 +17,7 @@ repositories { dependencies { implementation project(":mvc") - implementation "org.apache.tomcat.embed:tomcat-embed-core:10.1.13" +// implementation "org.apache.tomcat.embed:tomcat-embed-core:10.1.13" implementation "org.apache.tomcat.embed:tomcat-embed-jasper:10.1.13" implementation "ch.qos.logback:logback-classic:1.2.12" implementation "org.apache.commons:commons-lang3:3.13.0" diff --git a/app/src/main/java/com/techcourse/Application.java b/app/src/main/java/com/techcourse/Application.java index 500d3f2d8b..fad3aa0a9a 100644 --- a/app/src/main/java/com/techcourse/Application.java +++ b/app/src/main/java/com/techcourse/Application.java @@ -1,10 +1,10 @@ package com.techcourse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.util.stream.Stream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webmvc.org.springframework.web.servlet.TomcatStarter; public class Application { diff --git a/app/src/main/java/com/techcourse/DispatcherServlet.java b/app/src/main/java/com/techcourse/DispatcherServlet.java deleted file mode 100644 index 57f1ca82dc..0000000000 --- a/app/src/main/java/com/techcourse/DispatcherServlet.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.techcourse; - -import com.techcourse.exception.NoSuchHandlerFoundException; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; -import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping; -import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecution; -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 HandlerAdaptors handlerAdaptors; - - public DispatcherServlet() { - } - - @Override - public void init() { - handlerAdaptors = HandlerAdaptors.getInstance(); - } - - @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); - - try { - final var viewName = getViewName(request, response); - move(viewName, request, response); - } catch (Throwable e) { - log.error("Exception : {}", e.getMessage(), e); - throw new ServletException(e.getMessage()); - } - } - - private String getViewName(final HttpServletRequest request, final HttpServletResponse response) - throws Exception { - HandlerAdaptor handlerAdaptor = handlerAdaptors.getHandler(request); - Object handler = handlerAdaptor.getHandlerMapping(); - if (handler instanceof AnnotationHandlerMapping) { - AnnotationHandlerMapping annotationHandlerMapping = (AnnotationHandlerMapping) handler; - HandlerExecution handlerExecution = (HandlerExecution) annotationHandlerMapping.getHandler(request); - return handlerExecution.handle(request, response).getViewName(); - } - if (handler instanceof ManualHandlerMapping) { - ManualHandlerMapping manualHandlerMapping = (ManualHandlerMapping) handler; - Controller controller = manualHandlerMapping.getHandler(request.getRequestURI()); - return controller.execute(request, response); - } - log.error("Not Found"); - throw new NoSuchHandlerFoundException(); - } - - private void move(final String viewName, final HttpServletRequest request, final HttpServletResponse response) - throws Exception { - if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { - response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); - return; - } - - final var requestDispatcher = request.getRequestDispatcher(viewName); - requestDispatcher.forward(request, response); - } -} diff --git a/app/src/main/java/com/techcourse/DispatcherServletInitializer.java b/app/src/main/java/com/techcourse/DispatcherServletInitializer.java index 6e814cdd25..8101c4da0b 100644 --- a/app/src/main/java/com/techcourse/DispatcherServletInitializer.java +++ b/app/src/main/java/com/techcourse/DispatcherServletInitializer.java @@ -4,10 +4,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import web.org.springframework.web.WebApplicationInitializer; +import webmvc.org.springframework.web.servlet.mvc.DispatcherServlet; /** - * Base class for {@link WebApplicationInitializer} - * implementations that register a {@link DispatcherServlet} in the servlet context. + * Base class for {@link WebApplicationInitializer} implementations that register a {@link DispatcherServlet} in the + * servlet context. */ public class DispatcherServletInitializer implements WebApplicationInitializer { @@ -17,7 +18,8 @@ public class DispatcherServletInitializer implements WebApplicationInitializer { @Override public void onStartup(final ServletContext servletContext) { - final var dispatcherServlet = new DispatcherServlet(); + + final var dispatcherServlet = new DispatcherServlet(getClass().getPackageName()); final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet); if (registration == null) { diff --git a/app/src/main/java/com/techcourse/HandlerAdaptor.java b/app/src/main/java/com/techcourse/HandlerAdaptor.java deleted file mode 100644 index 87e503c2b6..0000000000 --- a/app/src/main/java/com/techcourse/HandlerAdaptor.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.techcourse; - -import java.lang.reflect.Field; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import web.org.springframework.web.bind.annotation.RequestMethod; -import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerKey; - -public class HandlerAdaptor { - private final T handler; - private final Set handlerKeys; - - private HandlerAdaptor(T handler, Set handlerKeys) { - this.handler = handler; - this.handlerKeys = handlerKeys; - } - - public static HandlerAdaptor of(T handler) throws NoSuchFieldException, IllegalAccessException { - final Field field = handler.getClass().getDeclaredField("controllers"); - field.setAccessible(true); - final Set handlerKeys = getHandlerKeys((Map) field.get(handler)); - return new HandlerAdaptor(handler, handlerKeys); - } - - private static Set getHandlerKeys(Map handlerKeys) { - if (handlerKeys.isEmpty()) { - return Set.of(); - } - if (handlerKeys.get(0) instanceof String) { - return handlerKeys.keySet().stream() - .map(url -> new HandlerKey((String) url, RequestMethod.GET)) - .collect(Collectors.toSet()); - } - return (Set) handlerKeys.keySet(); - } - - public boolean isHandle(HandlerKey handlerKey) { - return this.handlerKeys.contains(handlerKey); - } - - public T getHandlerMapping() { - return this.handler; - } -} diff --git a/app/src/main/java/com/techcourse/HandlerAdaptors.java b/app/src/main/java/com/techcourse/HandlerAdaptors.java deleted file mode 100644 index ed1c527585..0000000000 --- a/app/src/main/java/com/techcourse/HandlerAdaptors.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.techcourse; - -import com.techcourse.exception.HandlerFieldException; -import jakarta.servlet.http.HttpServletRequest; -import java.util.ArrayList; -import java.util.List; -import web.org.springframework.web.bind.annotation.RequestMethod; -import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping; -import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerKey; - -public class HandlerAdaptors { - - private static HandlerAdaptors instance; - - private static List> handlerAdaptors; - - private HandlerAdaptors() { - this.handlerAdaptors = new ArrayList<>(); - try { - final AnnotationHandlerMapping annotationHandlerMapping = new AnnotationHandlerMapping(); - annotationHandlerMapping.initialize(); - final ManualHandlerMapping manualHandlerMapping = new ManualHandlerMapping(); - manualHandlerMapping.initialize(); - handlerAdaptors.add(HandlerAdaptor.of(annotationHandlerMapping)); - handlerAdaptors.add(HandlerAdaptor.of(manualHandlerMapping)); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new HandlerFieldException(); - } - } - - public static synchronized HandlerAdaptors getInstance() { - if (instance == null) { - instance = new HandlerAdaptors(); - } - return instance; - } - - public HandlerAdaptor getHandler(HttpServletRequest httpServletRequest) { - final HandlerKey handlerKey = new HandlerKey( - httpServletRequest.getRequestURI(), - Enum.valueOf(RequestMethod.class, httpServletRequest.getMethod()) - ); - return handlerAdaptors.stream() - .filter(handlerAdaptor -> handlerAdaptor.isHandle(handlerKey)) - .findFirst() - .orElseThrow(); - } -} diff --git a/app/src/main/java/com/techcourse/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/ManualHandlerMapping.java deleted file mode 100644 index 23373b60fe..0000000000 --- a/app/src/main/java/com/techcourse/ManualHandlerMapping.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.techcourse; - -import com.techcourse.controller.LoginController; -import com.techcourse.controller.LoginViewController; -import com.techcourse.controller.LogoutController; -import java.util.HashMap; -import java.util.Map; -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; - -public class ManualHandlerMapping { - - private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); - - private static final Map controllers = new HashMap<>(); - - 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()); - - log.info("Initialized Handler Mapping!"); - controllers.keySet() - .forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass())); - } - - public Controller getHandler(final String requestURI) { - log.debug("Request Mapping Uri : {}", requestURI); - return controllers.get(requestURI); - } -} diff --git a/app/src/main/java/com/techcourse/controller/DefaultPathController.java b/app/src/main/java/com/techcourse/controller/DefaultPathController.java new file mode 100644 index 0000000000..99efb34af8 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/DefaultPathController.java @@ -0,0 +1,18 @@ +package com.techcourse.controller; + +import context.org.springframework.stereotype.Controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.view.JspView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; + +@Controller +public class DefaultPathController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public ModelAndView getDefaultView(HttpServletRequest request, HttpServletResponse response) { + return new ModelAndView(new JspView("/index.jsp")); + } +} diff --git a/app/src/main/java/com/techcourse/controller/LoginController.java b/app/src/main/java/com/techcourse/controller/LoginController.java index 0428fe109e..6f3aead120 100644 --- a/app/src/main/java/com/techcourse/controller/LoginController.java +++ b/app/src/main/java/com/techcourse/controller/LoginController.java @@ -2,28 +2,45 @@ import com.techcourse.domain.User; import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.view.JspView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; -public class LoginController implements Controller { +@Controller +public class LoginController { private static final Logger log = LoggerFactory.getLogger(LoginController.class); - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - if (UserSession.isLoggedIn(req.getSession())) { - return "redirect:/index.jsp"; + @RequestMapping(value = "/login/view", method = RequestMethod.GET) + public ModelAndView getLoginPage(HttpServletRequest req, HttpServletResponse res) { + final String viewName = UserSession.getUserFrom(req.getSession()) + .map(user -> { + log.info("logged in {}", user.getAccount()); + return "redirect:/index.jsp"; + }) + .orElse("/login.jsp"); + return new ModelAndView(new JspView(viewName)); + } + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public ModelAndView getLogin(HttpServletRequest request, HttpServletResponse response) { + if (UserSession.isLoggedIn(request.getSession())) { + return new ModelAndView(new JspView("redirect:/index.jsp")); } - return InMemoryUserRepository.findByAccount(req.getParameter("account")) + final String viewName = InMemoryUserRepository.findByAccount(request.getParameter("account")) .map(user -> { log.info("User : {}", user); - return login(req, user); + return login(request, user); }) .orElse("redirect:/401.jsp"); + return new ModelAndView(new JspView(viewName)); } private String login(final HttpServletRequest request, final User user) { diff --git a/app/src/main/java/com/techcourse/controller/LoginViewController.java b/app/src/main/java/com/techcourse/controller/LoginViewController.java deleted file mode 100644 index 86ec26cdce..0000000000 --- a/app/src/main/java/com/techcourse/controller/LoginViewController.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.techcourse.controller; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; - -public class LoginViewController implements Controller { - - private static final Logger log = LoggerFactory.getLogger(LoginViewController.class); - - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - return UserSession.getUserFrom(req.getSession()) - .map(user -> { - log.info("logged in {}", user.getAccount()); - return "redirect:/index.jsp"; - }) - .orElse("/login.jsp"); - } -} diff --git a/app/src/main/java/com/techcourse/controller/LogoutController.java b/app/src/main/java/com/techcourse/controller/LogoutController.java index 4642fd9450..4d139abf33 100644 --- a/app/src/main/java/com/techcourse/controller/LogoutController.java +++ b/app/src/main/java/com/techcourse/controller/LogoutController.java @@ -1,15 +1,20 @@ package com.techcourse.controller; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.view.JspView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; -public class LogoutController implements Controller { +@Controller +public class LogoutController { - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + @RequestMapping(value = "/logout", method = RequestMethod.GET) + public ModelAndView logout(final HttpServletRequest req, final HttpServletResponse res) throws Exception { final var session = req.getSession(); session.removeAttribute(UserSession.SESSION_KEY); - return "redirect:/"; + return new ModelAndView(new JspView("redirect:/")); } } diff --git a/app/src/main/java/com/techcourse/controller/RegisterController.java b/app/src/main/java/com/techcourse/controller/RegisterController.java index 7b602a4df3..fcc197b1a6 100644 --- a/app/src/main/java/com/techcourse/controller/RegisterController.java +++ b/app/src/main/java/com/techcourse/controller/RegisterController.java @@ -7,8 +7,8 @@ import jakarta.servlet.http.HttpServletResponse; import web.org.springframework.web.bind.annotation.RequestMapping; import web.org.springframework.web.bind.annotation.RequestMethod; -import webmvc.org.springframework.web.servlet.ModelAndView; import webmvc.org.springframework.web.servlet.view.JspView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; @Controller public class RegisterController { @@ -24,7 +24,7 @@ public ModelAndView save(HttpServletRequest req, HttpServletResponse res) { return modelAndView; } - @RequestMapping(value = "/register", method = RequestMethod.GET) + @RequestMapping(value = "/register/view", method = RequestMethod.GET) public ModelAndView show(HttpServletRequest req, HttpServletResponse res) { return new ModelAndView(new JspView("/register.jsp")); } 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 136962136d..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 webmvc.org.springframework.web.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/app/src/main/java/com/techcourse/controller/UserController.java b/app/src/main/java/com/techcourse/controller/UserController.java new file mode 100644 index 0000000000..60e14fecb5 --- /dev/null +++ b/app/src/main/java/com/techcourse/controller/UserController.java @@ -0,0 +1,32 @@ +package com.techcourse.controller; + +import com.techcourse.domain.User; +import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.view.JsonView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; + +@Controller +public class UserController { + + private static final Logger log = LoggerFactory.getLogger(UserController.class); + + @RequestMapping(value = "/api/user", method = RequestMethod.GET) + public ModelAndView show(HttpServletRequest request, HttpServletResponse response) { + final String account = request.getParameter("account"); + log.debug("user id : {}", account); + + final ModelAndView modelAndView = new ModelAndView(new JsonView()); + final User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + + modelAndView.addObject("user", user); + return modelAndView; + } +} diff --git a/app/src/main/java/com/techcourse/exception/HandlerFieldException.java b/app/src/main/java/com/techcourse/exception/HandlerFieldException.java deleted file mode 100644 index 75549bfa23..0000000000 --- a/app/src/main/java/com/techcourse/exception/HandlerFieldException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.techcourse.exception; - -public class HandlerFieldException extends RuntimeException { - - public HandlerFieldException() { - super("Handler needs Field name 'controllers'"); - } -} diff --git a/mvc/build.gradle b/mvc/build.gradle index 8cdd011952..6ed9c7a311 100644 --- a/mvc/build.gradle +++ b/mvc/build.gradle @@ -11,6 +11,7 @@ repositories { } dependencies { + implementation "org.apache.tomcat.embed:tomcat-embed-core:10.1.13" implementation "jakarta.servlet:jakarta.servlet-api:5.0.0" implementation "javax.servlet.jsp:javax.servlet.jsp-api:2.3.3" implementation "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0" diff --git a/app/src/main/java/com/techcourse/TomcatStarter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/TomcatStarter.java similarity index 95% rename from app/src/main/java/com/techcourse/TomcatStarter.java rename to mvc/src/main/java/webmvc/org/springframework/web/servlet/TomcatStarter.java index 4f26f228e3..4c591ce9d0 100644 --- a/app/src/main/java/com/techcourse/TomcatStarter.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/TomcatStarter.java @@ -1,13 +1,13 @@ -package com.techcourse; +package webmvc.org.springframework.web.servlet; +import java.io.File; 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; +import webmvc.org.springframework.web.servlet.exception.UncheckedServletException; public class TomcatStarter { diff --git a/app/src/main/java/com/techcourse/exception/NoSuchHandlerFoundException.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/exception/NoSuchHandlerFoundException.java similarity index 74% rename from app/src/main/java/com/techcourse/exception/NoSuchHandlerFoundException.java rename to mvc/src/main/java/webmvc/org/springframework/web/servlet/exception/NoSuchHandlerFoundException.java index 845d9d11a8..6e8ec12146 100644 --- a/app/src/main/java/com/techcourse/exception/NoSuchHandlerFoundException.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/exception/NoSuchHandlerFoundException.java @@ -1,4 +1,4 @@ -package com.techcourse.exception; +package webmvc.org.springframework.web.servlet.exception; public class NoSuchHandlerFoundException extends RuntimeException { diff --git a/app/src/main/java/com/techcourse/UncheckedServletException.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/exception/UncheckedServletException.java similarity index 71% rename from app/src/main/java/com/techcourse/UncheckedServletException.java rename to mvc/src/main/java/webmvc/org/springframework/web/servlet/exception/UncheckedServletException.java index 26acea7605..6fe99247f8 100644 --- a/app/src/main/java/com/techcourse/UncheckedServletException.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/exception/UncheckedServletException.java @@ -1,4 +1,4 @@ -package com.techcourse; +package webmvc.org.springframework.web.servlet.exception; public class UncheckedServletException extends RuntimeException { diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/DispatcherServlet.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/DispatcherServlet.java new file mode 100644 index 0000000000..2717fb374c --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/DispatcherServlet.java @@ -0,0 +1,47 @@ +package webmvc.org.springframework.web.servlet.mvc; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import webmvc.org.springframework.web.servlet.mvc.adaptor.HandlerAdaptors; +import webmvc.org.springframework.web.servlet.mvc.adaptor.HandlerMappings; +import webmvc.org.springframework.web.servlet.view.ModelAndView; + +public class DispatcherServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); + + private HandlerAdaptors handlerAdaptors; + private HandlerMappings handlerMappings; + private final Object[] basePackage; + + public DispatcherServlet(final Object... basePackage) { + this.basePackage = basePackage; + } + + @Override + public void init() { + handlerAdaptors = new HandlerAdaptors(); + handlerMappings = new HandlerMappings(basePackage); + } + + @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); + + try { + final Object handler = handlerMappings.getHandler(request); + final ModelAndView modelAndView = handlerAdaptors.execute(handler, request, response); + modelAndView.render(request, response); + } catch (Throwable e) { + log.error("Exception : {}", e.getMessage(), e); + throw new ServletException(e.getMessage()); + } + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/AnnotationHandlerAdaptor.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/AnnotationHandlerAdaptor.java new file mode 100644 index 0000000000..7bba6eaf9f --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/AnnotationHandlerAdaptor.java @@ -0,0 +1,22 @@ +package webmvc.org.springframework.web.servlet.mvc.adaptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.view.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecution; + +public class AnnotationHandlerAdaptor implements HandlerAdaptor { + + @Override + public boolean isHandle(Object handler) { + return handler instanceof HandlerExecution; + } + + @Override + public ModelAndView execute(Object handler, HttpServletRequest request, HttpServletResponse response) + throws Exception { + HandlerExecution handlerExecution = (HandlerExecution) handler; + Object result = handlerExecution.handle(request, response); + return ModelAndViewMapper.mapping(result); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerAdaptor.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerAdaptor.java new file mode 100644 index 0000000000..255c1a05c2 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerAdaptor.java @@ -0,0 +1,12 @@ +package webmvc.org.springframework.web.servlet.mvc.adaptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.view.ModelAndView; + +public interface HandlerAdaptor { + + boolean isHandle(Object handler); + + ModelAndView execute(Object handler, HttpServletRequest request, HttpServletResponse response) throws Exception; +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerAdaptors.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerAdaptors.java new file mode 100644 index 0000000000..62fadd358b --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerAdaptors.java @@ -0,0 +1,27 @@ +package webmvc.org.springframework.web.servlet.mvc.adaptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; +import webmvc.org.springframework.web.servlet.view.ModelAndView; + +public class HandlerAdaptors { + + private final List adaptors; + + public HandlerAdaptors() { + this.adaptors = new ArrayList<>(); + adaptors.add(new ManualHandlerAdaptor()); + adaptors.add(new AnnotationHandlerAdaptor()); + } + + public ModelAndView execute(Object handler, HttpServletRequest request, HttpServletResponse response) + throws Exception { + final HandlerAdaptor handlerAdaptor = adaptors.stream() + .filter(adaptor -> adaptor.isHandle(handler)) + .findFirst() + .orElseThrow(); + return handlerAdaptor.execute(handler, request, response); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerMappings.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerMappings.java new file mode 100644 index 0000000000..c6c7e7f6a8 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/HandlerMappings.java @@ -0,0 +1,24 @@ +package webmvc.org.springframework.web.servlet.mvc.adaptor; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Objects; +import webmvc.org.springframework.web.servlet.exception.NoSuchHandlerFoundException; +import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping; + +public class HandlerMappings { + +private final AnnotationHandlerMapping annotationHandlerMapping; + + public HandlerMappings(final Object... basePackage) { + this.annotationHandlerMapping = new AnnotationHandlerMapping(basePackage); + annotationHandlerMapping.initialize(); + } + + public Object getHandler(HttpServletRequest request) { + Object annotationHandler = annotationHandlerMapping.getHandler(request); + if (Objects.nonNull(annotationHandler)) { + return annotationHandler; + } + throw new NoSuchHandlerFoundException(); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/ManualHandlerAdaptor.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/ManualHandlerAdaptor.java new file mode 100644 index 0000000000..7be2b90eb1 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/ManualHandlerAdaptor.java @@ -0,0 +1,22 @@ +package webmvc.org.springframework.web.servlet.mvc.adaptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.view.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import webmvc.org.springframework.web.servlet.view.JspView; + +public class ManualHandlerAdaptor implements HandlerAdaptor { + + @Override + public boolean isHandle(Object handler) { + return handler instanceof Controller; + } + + @Override + public ModelAndView execute(Object handler, HttpServletRequest request, HttpServletResponse response) + throws Exception { + Controller controller = (Controller) handler; + return new ModelAndView(new JspView(controller.execute(request, response))); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/ModelAndViewMapper.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/ModelAndViewMapper.java new file mode 100644 index 0000000000..4bee3fb7a6 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/adaptor/ModelAndViewMapper.java @@ -0,0 +1,40 @@ +package webmvc.org.springframework.web.servlet.mvc.adaptor; + +import java.util.Map; +import java.util.Map.Entry; +import webmvc.org.springframework.web.servlet.view.JsonView; +import webmvc.org.springframework.web.servlet.view.JspView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; + +public class ModelAndViewMapper { + + private ModelAndViewMapper(){ + } + + public static ModelAndView mapping(Object result){ + if(result instanceof String){ + return new ModelAndView(new JspView((String) result)); + } + + if(result instanceof ModelAndView){ + return (ModelAndView) result; + } + + if (result instanceof String) { + return new ModelAndView(new JspView((String) result)); + } + + if(result instanceof Map){ + return getModelAndJsonView(((Map) result)); + } + throw new IllegalArgumentException(); + } + + private static ModelAndView getModelAndJsonView(Map result){ + final ModelAndView modelAndView = new ModelAndView(new JsonView()); + for(Entry entry: result.entrySet()){ + modelAndView.addObject((String) entry.getKey(),entry.getValue()); + } + return new ModelAndView(new JsonView()); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java index 6b384c357d..34bc51e848 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerExecution.java @@ -3,7 +3,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.lang.reflect.Method; -import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; public class HandlerExecution { diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java index 65002d1803..421a3aa13b 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java @@ -1,15 +1,24 @@ package webmvc.org.springframework.web.servlet.view; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.Writer; import java.util.Map; -import webmvc.org.springframework.web.servlet.View; public class JsonView implements View { + static final ObjectMapper objectMapper = new ObjectMapper(); + @Override public void render(final Map model, final HttpServletRequest request, HttpServletResponse response) throws Exception { + Writer writer = response.getWriter(); + if(model.size()==1){ + writer.write((String) model.values().toArray()[0]); + return; + } + objectMapper.writeValue(writer, model); } @Override diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java index b594fca29e..5e3a608063 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java @@ -5,7 +5,6 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import webmvc.org.springframework.web.servlet.View; public class JspView implements View { @@ -22,14 +21,18 @@ public JspView(final String viewName) { @Override public void render(final Map model, final HttpServletRequest request, final HttpServletResponse response) throws Exception { - // todo + if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { + response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); + return; + } model.keySet().forEach(key -> { log.debug("attribute name : {}, value : {}", key, model.get(key)); request.setAttribute(key, model.get(key)); }); - // todo + final var requestDispatcher = request.getRequestDispatcher(viewName); + requestDispatcher.forward(request, response); } @Override diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/ModelAndView.java similarity index 72% rename from mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java rename to mvc/src/main/java/webmvc/org/springframework/web/servlet/view/ModelAndView.java index e6ce362282..caa1b8ba08 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/ModelAndView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/ModelAndView.java @@ -1,5 +1,7 @@ -package webmvc.org.springframework.web.servlet; +package webmvc.org.springframework.web.servlet.view; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -23,6 +25,10 @@ public Object getObject(final String attributeName) { return model.get(attributeName); } + public void render(final HttpServletRequest request, HttpServletResponse response) throws Exception { + this.view.render(model, request, response); + } + public Map getModel() { return Collections.unmodifiableMap(model); } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/View.java similarity index 84% rename from mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java rename to mvc/src/main/java/webmvc/org/springframework/web/servlet/view/View.java index 09c45a7ad8..d0cd924d1a 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/View.java @@ -1,4 +1,4 @@ -package webmvc.org.springframework.web.servlet; +package webmvc.org.springframework.web.servlet.view; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/mvc/src/test/java/samples/TestController.java b/mvc/src/test/java/samples/TestController.java index 1f0e4acfb3..8b35617efc 100644 --- a/mvc/src/test/java/samples/TestController.java +++ b/mvc/src/test/java/samples/TestController.java @@ -7,7 +7,7 @@ import org.slf4j.LoggerFactory; import web.org.springframework.web.bind.annotation.RequestMapping; import web.org.springframework.web.bind.annotation.RequestMethod; -import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.ModelAndView; import webmvc.org.springframework.web.servlet.view.JspView; @Controller