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단계] 토리(천은정) 미션 제출합니다. #491

Merged
merged 6 commits into from
Sep 14, 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
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package cache.com.example.cachecontrol;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

@Configuration
public class CacheWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(final InterceptorRegistry registry) {
final WebContentInterceptor webContentInterceptor = new WebContentInterceptor();
final CacheControl cacheControl = CacheControl
.noCache()
.cachePrivate();
webContentInterceptor.addCacheMapping(cacheControl, "/**");
registry.addInterceptor(webContentInterceptor);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package cache.com.example.etag;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;

import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES;

@Configuration
public class EtagFilterConfiguration {

// @Bean
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
// return null;
// }
@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> filterFilterRegistrationBean
= new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
filterFilterRegistrationBean.addUrlPatterns("/etag", PREFIX_STATIC_RESOURCES + "/*");
return filterFilterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.Duration;

@Configuration
public class CacheBustingWebConfig implements WebMvcConfigurer {

Expand All @@ -20,6 +23,7 @@ public CacheBustingWebConfig(ResourceVersion version) {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**")
.addResourceLocations("classpath:/static/");
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic());
}
}
3 changes: 3 additions & 0 deletions study/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ handlebars:
suffix: .html

server:
compression:
enabled: true
min-response-size: 10
tomcat:
accept-count: 1
max-connections: 1
Expand Down
6 changes: 3 additions & 3 deletions study/src/test/java/thread/stage0/SynchronizationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다.
* 자바는 공유 데이터에 대한 스레드 접근을 동기화(synchronization)하여 경쟁 조건을 방지한다.
* 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다.
*
* <p>
* Synchronization
* https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
*/
Expand All @@ -21,7 +21,7 @@ class SynchronizationTest {
/**
* 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자.
* synchronized 키워드에 대하여 찾아보고 적용하면 된다.
*
* <p>
* Guide to the Synchronized Keyword in Java
* https://www.baeldung.com/java-synchronized
*/
Expand All @@ -41,7 +41,7 @@ private static final class SynchronizedMethods {

private int sum = 0;

public void calculate() {
public synchronized void calculate() {
setSum(getSum() + 1);
}

Expand Down
10 changes: 5 additions & 5 deletions study/src/test/java/thread/stage0/ThreadPoolsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
/**
* 스레드 풀은 무엇이고 어떻게 동작할까?
* 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자.
*
* <p>
* Thread Pools
* https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
*
* <p>
* Introduction to Thread Pools in Java
* https://www.baeldung.com/thread-pool-java-and-guava
*/
Expand All @@ -31,8 +31,8 @@ void testNewFixedThreadPool() {
executor.submit(logWithSleep("hello fixed thread pools"));

// 올바른 값으로 바꿔서 테스트를 통과시키자.
final int expectedPoolSize = 0;
final int expectedQueueSize = 0;
final int expectedPoolSize = 2;
final int expectedQueueSize = 1;

assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
Expand All @@ -46,7 +46,7 @@ void testNewCachedThreadPool() {
executor.submit(logWithSleep("hello cached thread pools"));

// 올바른 값으로 바꿔서 테스트를 통과시키자.
final int expectedPoolSize = 0;
final int expectedPoolSize = 3;
final int expectedQueueSize = 0;

assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
Expand Down
6 changes: 6 additions & 0 deletions tomcat/src/main/java/nextstep/Application.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package nextstep;

import nextstep.jwp.controller.HomeController;
import nextstep.jwp.controller.LoginController;
import nextstep.jwp.controller.RegisterController;
import org.apache.catalina.startup.Tomcat;

public class Application {

public static void main(String[] args) {
final var tomcat = new Tomcat();
tomcat.addController("/", new HomeController());
tomcat.addController("/login", new LoginController());
tomcat.addController("/register", new RegisterController());
tomcat.start();
}
}
17 changes: 17 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/HomeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package nextstep.jwp.controller;

import org.apache.coyote.http11.controller.AbstractController;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public class HomeController extends AbstractController {
@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
throw new UnsupportedOperationException();
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
response.home("Hello world!");
}
}
57 changes: 57 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,57 @@
package nextstep.jwp.controller;

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.common.HttpUri;
import org.apache.coyote.http11.controller.AbstractController;
import org.apache.coyote.http11.cookie.HttpCookie;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.session.HttpSession;
import org.apache.coyote.http11.session.SessionManager;

import java.util.Map;
import java.util.Objects;

public class LoginController extends AbstractController {
private static final SessionManager sessionManager = SessionManager.create();
private static final String USER = "user";
private static final String PASSWORD = "password";

@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
final Map<String, String> loginData = request.getBody();
final User user = InMemoryUserRepository.findByAccount(loginData.get("account"))
.orElse(null);
if (!Objects.isNull(user) && user.checkPassword(loginData.get(PASSWORD))) {
final HttpCookie newCookie = HttpCookie.create();
saveSession(newCookie, user);
response.found(HttpUri.INDEX_HTML.getUri());
response.setCookie(newCookie.getJSESSIONID());
return;
}
response.ok(HttpUri.UNAUTHORIZED_HTML.getUri());
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) {
final HttpCookie cookie = makeHttpCookie(request);
if (cookie.hasJSESSIONID()) {
final String jsessionid = cookie.getJSESSIONID();
final HttpSession httpSession = sessionManager.findSession(jsessionid);
if (Objects.isNull(httpSession)) {
response.ok(HttpUri.LOGIN_HTML.getUri());
return;
}
response.found(HttpUri.INDEX_HTML.getUri());
return;
}
response.ok(HttpUri.LOGIN_HTML.getUri());
}

private void saveSession(final HttpCookie newCookie, final User user) {
final HttpSession httpSession = new HttpSession(newCookie.getJSESSIONID());
httpSession.setAttribute(USER, user);
sessionManager.add(httpSession);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nextstep.jwp.controller;

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.common.HttpUri;
import org.apache.coyote.http11.controller.AbstractController;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

import java.util.Map;

public class RegisterController extends AbstractController {
@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
final Map<String, String> registerData = request.getBody();
InMemoryUserRepository.save(new User(registerData.get("account"), registerData.get("password"), registerData.get("email")));
response.ok(HttpUri.INDEX_HTML.getUri());
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
response.ok(HttpUri.REGISTER.getUri());
}
}
20 changes: 13 additions & 7 deletions tomcat/src/main/java/org/apache/catalina/connector/Connector.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
package org.apache.catalina.connector;

import org.apache.coyote.http11.Http11Processor;
import org.apache.coyote.http11.controller.FrontController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Connector implements Runnable {

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

private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private static final int MAX_THREAD_POOL = 250;

private final ServerSocket serverSocket;
private final ExecutorService executorService;
private final FrontController frontController;

Choose a reason for hiding this comment

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

FrontController를 싱글톤 형식으로 관리하기 위한 방법으론 Http11Processor에서 private static final로 가지는 방법도 있었을 것 같은데, Connector의 인스턴스 변수로 가지는 방법을 택한 이유가 무엇인지 궁금합니다 :)

private boolean stopped;

public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
public Connector(FrontController frontController) {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, MAX_THREAD_POOL, frontController);
}

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

private ServerSocket createServerSocket(final int port, final int acceptCount) {
Expand Down Expand Up @@ -66,8 +72,8 @@ private void process(final Socket connection) {
if (connection == null) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
var processor = new Http11Processor(connection, frontController);
executorService.submit(processor);
}

public void stop() {
Expand Down
15 changes: 13 additions & 2 deletions tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package org.apache.catalina.startup;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.controller.Controller;
import org.apache.coyote.http11.controller.FrontController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

public class Tomcat {

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

private final FrontController frontController;

public Tomcat() {
this.frontController = new FrontController();
}

public void start() {
var connector = new Connector();
var connector = new Connector(frontController);
connector.start();

try {
Expand All @@ -24,4 +31,8 @@ public void start() {
connector.stop();
}
}

public void addController(final String path, final Controller controller) {
frontController.addController(path, controller);
}
}
Loading