Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[톰캣 구현하기 - 3,4단계] 도기(김동호) 미션 제출합니다. #449

Merged
merged 15 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/application/LoginService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nextstep.jwp.application;

import static nextstep.jwp.db.InMemoryUserRepository.findByAccount;

import nextstep.jwp.exception.InvalidPasswordException;
import nextstep.jwp.model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoginService {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A

단순 질문입니다!
저 같은 경우는 로그인/혹은 회원가입 로직이 비대하지 않다고 판단해 컨트롤러 영역에 위치시켰는데, 서비스 클래스를 도입하신 이유가 있을까요??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

물론 하마드 말씀대로 로직 자체는 비대하지 않지만, 습관적으로 분리한 것 같아요 🤣


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

public User login(String account, String password) {
User user = findByAccount(account).orElseThrow();

if (!user.checkPassword(password)) {
log.debug("비밀번호 틀림! - 입력={}", password);
throw new InvalidPasswordException();
}
log.info("로그인 성공! - {}", user);
return user;
}
}
17 changes: 17 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/application/RegisterService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package nextstep.jwp.application;

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RegisterService {

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

public void register(String account, String email, String password) {
User user = new User(account, password, email);
InMemoryUserRepository.save(user);
log.info("회원가입 성공! - {}", user);
}
}
23 changes: 23 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/HelloController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nextstep.jwp.controller;

import static org.apache.coyote.HttpStatus.OK;
import static org.apache.coyote.header.HttpHeaders.CONTENT_LENGTH;
import static org.apache.coyote.header.HttpHeaders.CONTENT_TYPE;

import org.apache.coyote.HttpRequest;
import org.apache.coyote.HttpResponse;
import org.apache.coyote.http11.handler.AbstractController;

public class HelloController extends AbstractController {

@Override
protected void doGet(HttpRequest request, HttpResponse response) {
String message = "Hello world!";

response.setVersion(request.protocolVersion());
response.setStatus(OK);
response.addHeader(CONTENT_TYPE, "text/html;charset=utf-8");
response.addHeader(CONTENT_LENGTH, String.valueOf(message.length()));
response.setBody(message);
}
}
62 changes: 62 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/LoginController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package nextstep.jwp.controller;

import static org.apache.coyote.HttpStatus.FOUND;
import static org.apache.coyote.HttpStatus.OK;
import static org.apache.coyote.header.HttpHeaders.CONTENT_LENGTH;
import static org.apache.coyote.header.HttpHeaders.CONTENT_TYPE;
import static org.apache.coyote.header.HttpHeaders.LOCATION;
import static org.apache.coyote.header.HttpHeaders.SET_COOKIE;

import java.util.Map;
import nextstep.jwp.application.LoginService;
import nextstep.jwp.model.User;
import nextstep.jwp.util.ResourceLoaderUtil;
import org.apache.coyote.HttpRequest;
import org.apache.coyote.HttpResponse;
import org.apache.coyote.header.ContentType;
import org.apache.coyote.header.HttpCookie;
import org.apache.coyote.http11.handler.AbstractController;
import org.apache.coyote.session.Session;
import org.apache.coyote.session.SessionManager;

public class LoginController extends AbstractController {

private final LoginService loginService = new LoginService();

@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
response.setVersion(request.protocolVersion());
if (request.getSession() != null) {
response.setStatus(FOUND);
response.addHeader(LOCATION, "index.html");
return;
}

String content = ResourceLoaderUtil.loadContent(request.requestUrl());
response.setStatus(OK);
response.addHeader(CONTENT_TYPE, ContentType.negotiate(request.requestUrl()));
response.addHeader(CONTENT_LENGTH, String.valueOf(content.getBytes().length));
response.setBody(content);
}

@Override
protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
Map<String, String> requestBody = request.getRequestBody();

response.setVersion(request.protocolVersion());
response.setStatus(FOUND);
try {
User user = loginService.login(requestBody.get("account"), requestBody.get("password"));
response.addHeader(LOCATION, "index.html");

Session session = new Session();
session.setAttribute(user.getAccount(), user);
SessionManager.add(session);
HttpCookie cookie = new HttpCookie();
cookie.addSessionId(session.id());
response.addHeader(SET_COOKIE, cookie.toString());
} catch (RuntimeException e) {
response.addHeader(LOCATION, "401.html");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package nextstep.jwp.controller;

import static org.apache.coyote.HttpStatus.FOUND;
import static org.apache.coyote.HttpStatus.OK;
import static org.apache.coyote.header.HttpHeaders.CONTENT_LENGTH;
import static org.apache.coyote.header.HttpHeaders.CONTENT_TYPE;
import static org.apache.coyote.header.HttpHeaders.LOCATION;

import java.util.Map;
import nextstep.jwp.application.RegisterService;
import nextstep.jwp.util.ResourceLoaderUtil;
import org.apache.coyote.HttpRequest;
import org.apache.coyote.HttpResponse;
import org.apache.coyote.header.ContentType;
import org.apache.coyote.http11.handler.AbstractController;

public class RegisterController extends AbstractController {

private final RegisterService registerService = new RegisterService();

@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
String content = ResourceLoaderUtil.loadContent(request.requestUrl());
response.setVersion(request.protocolVersion());
response.setStatus(OK);
response.addHeader(CONTENT_TYPE, ContentType.negotiate(request.requestUrl()));
response.addHeader(CONTENT_LENGTH, String.valueOf(content.getBytes().length));
response.setBody(content);
}

@Override
protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
Map<String, String> requestBody = request.getRequestBody();
registerService.register(
requestBody.get("account"),
requestBody.get("email"),
requestBody.get("password")
);

response.setVersion(request.protocolVersion());
response.setStatus(FOUND);
response.addHeader(LOCATION, "index.html");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nextstep.jwp.controller;

import static org.apache.coyote.HttpStatus.OK;
import static org.apache.coyote.header.HttpHeaders.CONTENT_LENGTH;
import static org.apache.coyote.header.HttpHeaders.CONTENT_TYPE;

import nextstep.jwp.util.ResourceLoaderUtil;
import org.apache.coyote.HttpRequest;
import org.apache.coyote.HttpResponse;
import org.apache.coyote.header.ContentType;
import org.apache.coyote.http11.handler.AbstractController;

public class ResourceController extends AbstractController {

@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
String content = ResourceLoaderUtil.loadContent(request.requestUrl());
response.setVersion(request.protocolVersion());
response.setStatus(OK);
response.addHeader(CONTENT_TYPE, ContentType.negotiate(request.requestUrl()));
response.addHeader(CONTENT_LENGTH, String.valueOf(content.getBytes().length));
response.setBody(content);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.jwp.exception;

public class InvalidPasswordException extends RuntimeException {

public InvalidPasswordException() {
super("비밀번호가 틀렸습니다.");
}
}
33 changes: 33 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/util/ResourceLoaderUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package nextstep.jwp.util;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ResourceLoaderUtil {
kdkdhoho marked this conversation as resolved.
Show resolved Hide resolved

private static final Logger log = LoggerFactory.getLogger(ResourceLoaderUtil.class);
private static final String DEFAULT_ROOT_PATH = "static";

private ResourceLoaderUtil() {
}

public static String loadContent(String path) {
if (!path.contains(".")) {
path += ".html";
}

URL resourcePath = ResourceLoaderUtil.class.getClassLoader().getResource(DEFAULT_ROOT_PATH + path);
String content = "";
try {
content = new String(Files.readAllBytes(new File(resourcePath.getFile()).toPath()));
} catch (IOException e) {
log.error(e.getMessage());
}
return content;
}

}
22 changes: 0 additions & 22 deletions tomcat/src/main/java/org/apache/catalina/SessionManager.java

This file was deleted.

33 changes: 20 additions & 13 deletions tomcat/src/main/java/org/apache/catalina/connector/Connector.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import java.io.UncheckedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.coyote.http11.Http11Processor;
import org.apache.coyote.http11.adaptor.ControllerMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -14,23 +17,27 @@ public class Connector implements Runnable {

private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private final ControllerMapping controllerMapping;
private final ExecutorService executorService;

private final ServerSocket serverSocket;
private boolean stopped;

public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
public Connector(ControllerMapping controllerMapping, int maxThreads) {
this(controllerMapping, DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, maxThreads);
}

public Connector(final int port, final int acceptCount) {
public Connector(ControllerMapping controllerMapping, int port, int acceptCount, int maxThreads) {
this.controllerMapping = controllerMapping;
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
this.executorService = Executors.newFixedThreadPool(maxThreads);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A

이건 그냥 참고사항인데요!
LMS "생각해보기"에 보시면,

최대 ThradPool의 크기는 250, 모든 Thread가 사용 중인(Busy) 상태이면 100명까지 대기 상태로 만들려면 어떻게 할까?
라는 문장이 있어요!

Executors.newFixedThreadPool(maxThreads);
를 사용하면 작업 큐의 사이즈가 Ìnteger.MAX_VALUE` 로 설정이 되는데,
어떻게 이걸 100으로 할 수 있을지 생각? 정도만 해보셔도 좋을 것 같네요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!! 해당 방법에 대해 생각해보고 적용까지 한번 해볼게요 :)

}

private ServerSocket createServerSocket(final int port, final int acceptCount) {
private ServerSocket createServerSocket(int port, int acceptCount) {
try {
final int checkedPort = checkPort(port);
final int checkedAcceptCount = checkAcceptCount(acceptCount);
int checkedPort = checkPort(port);
int checkedAcceptCount = checkAcceptCount(acceptCount);
return new ServerSocket(checkedPort, checkedAcceptCount);
} catch (IOException e) {
throw new UncheckedIOException(e);
Expand Down Expand Up @@ -60,12 +67,12 @@ private void connect() {
}
}

private void process(final Socket connection) {
private void process(Socket connection) {
if (connection == null) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
var processor = new Http11Processor(connection, controllerMapping);
executorService.submit(processor);
}

public void stop() {
Expand All @@ -77,17 +84,17 @@ public void stop() {
}
}

private int checkPort(final int port) {
final var MIN_PORT = 1;
final var MAX_PORT = 65535;
private int checkPort(int port) {
var MIN_PORT = 1;
var MAX_PORT = 65535;

if (port < MIN_PORT || MAX_PORT < port) {
return DEFAULT_PORT;
}
return port;
}

private int checkAcceptCount(final int acceptCount) {
private int checkAcceptCount(int acceptCount) {
return Math.max(acceptCount, DEFAULT_ACCEPT_COUNT);
}
}
5 changes: 4 additions & 1 deletion tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

import java.io.IOException;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.adaptor.ControllerMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Tomcat {

private static final Logger log = LoggerFactory.getLogger(Tomcat.class);
private static final int DEFAULT_MAX_THREADS = 200;
private final ControllerMapping controllerMapping = new ControllerMapping();

public void start() {
var connector = new Connector();
var connector = new Connector(controllerMapping, DEFAULT_MAX_THREADS);
connector.start();

try {
Expand Down
Loading