├── .gitignore ├── README.md ├── img ├── directory.jpg └── tomcatarchitecture2.png └── src ├── ChuYeonHoon ├── Chap03 │ ├── README.md │ └── img01.jpg ├── Chap04 │ ├── README.md │ └── img01.jpg ├── Chap05 │ ├── README.md │ ├── img01.jpg │ ├── img02.jpg │ ├── img03.jpg │ └── img04.jpg └── Chap09 │ ├── README.md │ ├── img01.jpg │ ├── img02.jpg │ └── img03.jpg ├── ImSoobin ├── Chap01 │ └── README.md ├── Chap02 │ ├── Constants.java │ ├── HttpServer1.java │ ├── HttpServer2.java │ ├── PrimitiveServlet.java │ ├── README.md │ ├── Request.java │ ├── RequestFacade.java │ ├── Response.java │ ├── ResponseFacade.java │ ├── ServletProcessor1.java │ ├── ServletProcessor2.java │ ├── StaticResourceProcessor.java │ └── img │ │ └── chap2.jpg ├── Chap07 │ ├── Constants.java │ ├── FileLogger.java │ ├── LoggerBase.java │ ├── README.md │ ├── SystemErrLogger.java │ ├── SystemOutLogger.java │ ├── img │ │ └── tomcatloger.jpg │ └── startup │ │ └── Bootstrap.java └── Chap12 │ ├── README.md │ └── StandardContext.java ├── NamGiUng ├── Chap03 │ ├── README.md │ └── img │ │ ├── Flow.png │ │ └── UML03.jpg └── Chap08 │ ├── README.md │ └── img │ ├── JVM.png │ ├── diagram.jpg │ └── img01.png ├── YangDonghwa ├── example │ └── ch02 │ │ ├── Constants.java │ │ ├── HttpServer1.java │ │ ├── HttpServer2.java │ │ ├── Request.java │ │ ├── RequestFacade.java │ │ ├── Response.java │ │ ├── ResponseFacade.java │ │ ├── ServletProcessor1.java │ │ ├── ServletProcessor2.java │ │ └── StaticResourceProcessor.java └── markdown │ ├── 00. 들어가기.md │ ├── 01. 간단한 웹 서버.md │ ├── 02. 간단한 서블릿 컨테이너.md │ ├── 03. 커넥터.md │ ├── 04. 톰캣의 기본 커넥터.md │ ├── 05. 컨테이너.md │ ├── 06. 생명주기.md │ ├── 07. 로거.md │ ├── 08. 로더.md │ ├── 09. 세션 관리.md │ ├── 10. 보안.md │ ├── 11. StandardWrapper.md │ └── img │ ├── 11-1.jpg │ └── 11-2.jpg └── YuJaeGuk ├── Chap01 └── README.md ├── Chap02 └── README.md ├── Chap03 └── README.md ├── Chap04 └── README.md └── Chap10 ├── LoginConfig.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | # Created by https://www.gitignore.io/api/windows,eclipse 3 | # Edit at https://www.gitignore.io/?templates=windows,eclipse 4 | 5 | ### Eclipse ### 6 | .metadata 7 | bin/ 8 | tmp/ 9 | *.tmp 10 | *.bak 11 | *.swp 12 | *~.nib 13 | local.properties 14 | .settings/ 15 | .loadpath 16 | .recommenders 17 | 18 | # External tool builders 19 | .externalToolBuilders/ 20 | 21 | # Locally stored "Eclipse launch configurations" 22 | *.launch 23 | 24 | # PyDev specific (Python IDE for Eclipse) 25 | *.pydevproject 26 | 27 | # CDT-specific (C/C++ Development Tooling) 28 | .cproject 29 | 30 | # CDT- autotools 31 | .autotools 32 | 33 | # Java annotation processor (APT) 34 | .factorypath 35 | 36 | # PDT-specific (PHP Development Tools) 37 | .buildpath 38 | 39 | # sbteclipse plugin 40 | .target 41 | 42 | # Tern plugin 43 | .tern-project 44 | 45 | # TeXlipse plugin 46 | .texlipse 47 | 48 | # STS (Spring Tool Suite) 49 | .springBeans 50 | 51 | # Code Recommenders 52 | .recommenders/ 53 | 54 | # Annotation Processing 55 | .apt_generated/ 56 | 57 | # Scala IDE specific (Scala & Java development for Eclipse) 58 | .cache-main 59 | .scala_dependencies 60 | .worksheet 61 | 62 | ### Eclipse Patch ### 63 | # Eclipse Core 64 | .project 65 | 66 | # JDT-specific (Eclipse Java Development Tools) 67 | .classpath 68 | 69 | # Annotation Processing 70 | .apt_generated 71 | 72 | .sts4-cache/ 73 | 74 | ### Windows ### 75 | # Windows thumbnail cache files 76 | Thumbs.db 77 | ehthumbs.db 78 | ehthumbs_vista.db 79 | 80 | # Dump file 81 | *.stackdump 82 | 83 | # Folder config file 84 | [Dd]esktop.ini 85 | 86 | # Recycle Bin used on file shares 87 | $RECYCLE.BIN/ 88 | 89 | # Windows Installer files 90 | *.cab 91 | *.msi 92 | *.msix 93 | *.msm 94 | *.msp 95 | 96 | # Windows shortcuts 97 | *.lnk 98 | 99 | # End of https://www.gitignore.io/api/windows,eclipse -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How-Tomcat-Works-Study 2 | ### Cafe24 톰캣 최종분석 스터디 문서 3 | ### - 기간 : 19.05.06 ~ 19.05.17 (2주) 4 | ### - 인원 : 임수빈, 남기웅, 양동화, 유재국, 추연훈 (5명) 5 | ### - 교재 : 톰캣 최종분석, 자바고양이Tomcat이야기 6 | ### Study Role 7 | __- 발표 방식__ 8 | 9 | 1일 3챕터씩 공부 후 1일 챕터별로 발표자 정해서 발표 10 | 11 | ## 톰캣최종분석 12 | ### 톰캣 구조 13 | ![Tomcat Architecture](./img/tomcatarchitecture2.png) 14 | ### 톰캣 디렉토리 구조 15 | ![directory](./img/directory.jpg) 16 | #### 0장 - [들어가기](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/YangDonghwa/markdown/00.%20%EB%93%A4%EC%96%B4%EA%B0%80%EA%B8%B0.md) 17 | 18 | #### 1장 - [간단한 웹 서버](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/YangDonghwa/markdown/01.%20%EA%B0%84%EB%8B%A8%ED%95%9C%20%EC%9B%B9%20%EC%84%9C%EB%B2%84.md) 19 | 20 | #### 2장 - [간단한 서블릿 컨테이너](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/ImSoobin/Chap02/README.md) 21 | 22 | #### 3장 - [커넥터]() 23 | 24 | #### 4장 - [톰캣의 기본 커넥터](https://github.com/Soobinnn/How-Tomcat-Works-Study/tree/master/src/YuJaeGuk/Chap04/README.md) 25 | 26 | #### 5장 - [컨테이너](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/ChuYeonHoon/Chap05/README.md) 27 | 28 | #### 6장 - [생명주기](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/YangDonghwa/markdown/06.%20%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0.md) 29 | 30 | #### 7장 - [로거](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/ImSoobin/Chap07/README.md) 31 | 32 | #### 8장 - [로더]() 33 | 34 | #### 9장 - [세션 관리](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/ChuYeonHoon/Chap09/README.md) 35 | 36 | #### 10장 - [보안](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/YuJaeGuk/Chap10/README.md) 37 | 38 | #### 11장 - [StandardWrapper](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/YangDonghwa/markdown/11.%20StandardWrapper.md) 39 | 40 | #### 12장 - [StandardContext](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/ImSoobin/Chap12/README.md) 41 | 42 | #### 13장 - 호스트와 엔진 43 | 44 | #### 14장 - 서버와 서비스 45 | 46 | #### 15장 - 다이제스터 47 | 48 | #### 16장 - 종료 후크 49 | 50 | #### 17장 - 톰캣의 기동 51 | 52 | #### 18장 - 배치자 53 | 54 | #### 19장 - 관리자 서블릿 55 | 56 | #### 20장 - JMX 기반의 관리 57 | -------------------------------------------------------------------------------- /img/directory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/img/directory.jpg -------------------------------------------------------------------------------- /img/tomcatarchitecture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/img/tomcatarchitecture2.png -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap03/README.md: -------------------------------------------------------------------------------- 1 | # 3장 커넥터 2 | ### 작성자 3 | 4 | **추연훈** 5 | 6 | ## 목표 7 | 8 | - HTTP 요청 헤더를 파싱해 서블릿이 헤더, 쿠키, 파라미터 이름/값 등의 정보를 받을 수 있게 커넥터를 만든다. 9 | - `Response` 클래스의 getWriter 메소드를 구현한다. 10 | 11 |
12 | 13 | ## StringManager 클래스 14 | 15 | - 톰캣은 특정 패키지마다 프로퍼티 파일을 만들고 이곳에 저장된 에러 메시지를 사용함으로써 에러 메시지 편집을 용이하게 만들었다. 그리고 이러한 **프로퍼티 파일은 모두 `org.apache.catalina.util.StringManager` 클래스의 인스턴스가 처리**한다. 따라서 톰캣이 구동되면 특정 패키지마다 존재하는 프로퍼티 파일을 읽기 위해 `StringManager` 인스턴스가 여러개 생성된다. 16 | 17 | - 자원낭비를 줄이기 위해 `StringManager` 클래스는 **동일한 패키지 안의 모든 클래스가 공유**해 사용하도록 설계됐다. 18 | 19 | - `StringManager` 클래스는 **싱글톤 클래스로 구현**되었으며 `StringManager`의 **정적 메소드인 getManager에 패키지 명을 전달해 호출**한다. 20 | 21 | ```java 22 | //private으로 생성된 하나의 생성자를 두기 때문에 외부에서 new 키워드로 생성이 불가능하다. 23 | private static Hashtable managers = new Hashtable(); 24 | 25 | //getManager메소드를 통해 자신의 인스턴스를 생성한다. 26 | public synchronized static StringManager getManager(String packageName){ 27 | 28 | StringManager mgr = (StringManager) managers.get(packageName); 29 | 30 | if (mgr = null){ 31 | mgr = new StringManager(packageName); 32 | managers.put(packageName,mgr); 33 | } 34 | 35 | return mgr; 36 | } 37 | ``` 38 | 39 |
40 | 41 | ## 애플리케이션 42 | 43 | ![img1](./img01.jpg) 44 | 45 | - HTTP 요청을 기다리는 역할은 `HttpConnector` 인스턴스가, 요청 및 응답 객체를 생성하는 역할은 `HttpProcessor`가 담당한다. 46 | - HTTP 요청은 `javax.servlet.http.HttpServletRequest`를 구현하는 `HttpRequest`에 해당된다. `HttpRequest` 객체는 `HttpServletRequest` 인스턴스로 캐스팅돼 서블릿의 service 메소드로 전달된다. 47 | - HTTP 요청의 파싱에는 문자열 처리와 연산이 매우 많이 수행되므로, 서블릿에서 필요로 하는 값만 파싱하게 되면 CPU 자원을 상당히 절약할 수 있다. 48 | - `HttpProcessor` 클래스는 자신의 parse 메소드를 사용해 HTTP 요청의 요청 라인과 헤더를 파싱한다. 파싱된 결과 값은 `HttpProcessor` 객체의 필드에 할당된다. 49 | 50 |
51 | 52 | #### 애플리케이션의 구동 53 | 54 | - Bootstrap 클래스를 통해 애플리케이션을 시작한다. 55 | 56 | > `Bootstrap` 클래스 57 | 58 | ```java 59 | public final class Bootstrap{ 60 | public static void main(String[] args){ 61 | HttpConnector connector = new HttpConnector(); 62 | connector.start(); 63 | } 64 | } 65 | ``` 66 | 67 |
68 | 69 | #### 커넥터 70 | - `HttpConnector` 클래스는 들어오는 HTTP 요청을 기다렸다가 서버 소켓을 생성하는 역할을 한다. 71 | 72 | > `HttpConnector` 클래스 73 | 74 | ```java 75 | public class HttpConnector implements Runnable{ 76 | boolean stopped; 77 | private String scheme = "http"; 78 | 79 | public String getScheme(){ 80 | return scheme; 81 | } 82 | 83 | public void run(){ 84 | ServerSocket serverSocket = null; 85 | int port = 8080; 86 | 87 | try{ 88 | // 서버 소켓 생성 89 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); 90 | 91 | } 92 | catch(IOException e){ 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | 97 | while(!stopped){ 98 | // HTTP 요청을 기다린다. 99 | Socket socket = null; 100 | 101 | try{ 102 | socket = serverSocket.accept(); 103 | 104 | } 105 | catch(Exception e){ 106 | continue; 107 | } 108 | 109 | // 각 요청에 대해 HttpProcessor의 인스턴스를 생성한다. 110 | // HttpProcessor의 process 메소드를 호출한다. 111 | HttpProcessor processor = new HttpProcessor(this); 112 | processor.process(socket); 113 | } 114 | 115 | public void start(){ 116 | Thread thread = new Thread(this); 117 | thread.start(); 118 | } 119 | } 120 | } 121 | ``` 122 | 123 |
124 | 125 | - `HttpProcessor` 클래스의 process 메소드는 들어오는 HTTP 요청에서 소켓을 받는다. 126 | 127 | 128 | > `HttpProcessor` 클래스의 process 메소드 129 | 130 | ```java 131 | public void process(Socket socket){ 132 | SocketInputStream input = null; 133 | OutputStream output = null; 134 | 135 | try{ 136 | input = new SocketInputStream(socket.getInputStream(), 2048); 137 | output = socket.getOutputStream(); 138 | 139 | // HttpRequest 객체의 생성 및 파싱 140 | request = new HttpRequest(input); 141 | 142 | // HttpResponse 객체의 생성 143 | response = new HttpResponse(output); 144 | response.setRequest(request); // 헤더 정보를 클라이언트에 전달할 수 있다. 145 | 146 | response.setHeader("Server", "Pyrmont Servlet Container"); 147 | parseRequest(input, output); 148 | parseHeaders(input); 149 | 150 | // 서블릿에 대한 요청인지 정적 자원에 대한 요청인지 확인 151 | // 서블릿에 대한 요청은 "/servlet/"으로 시작 152 | if(request.getRequestURI().startsWith("/servlet/")){ 153 | ServletProcessor processor = new ServletProcessor(); 154 | processor.process(request, response); 155 | } 156 | else{ 157 | StaticResourceProcessor processor = new StaticResourceProcessor(); 158 | processor.process(request, response); 159 | } 160 | 161 | // 소켓 닫기 162 | socket.close(); 163 | // 종료 명령은 존재하지 않음. 164 | 165 | } 166 | catch (Exception e){ 167 | e.printStackTrace(); 168 | 169 | } 170 | } 171 | ``` 172 | 173 | 174 |
175 | 176 | #### HttpRequest 객체 생성허긔 177 | 178 | - `HttpRequest` 클래스에 들어오는 HTTP 요청의 헤더, 쿠키 및 파라미터 값은 다음의 참조 변수에 저장 된다. 179 | 180 | ``` java 181 | protected Map<,> headers = new HashMap<>(); 182 | protected List<> cookies = new ArrayList<>(); 183 | protected ParameterMap parameters = null; 184 | 185 | ``` 186 | 187 | - HTTP 요청을 파싱하는 작업은 다음과 같이 분류되어 진행된다. 188 | 1. 소켓의 입력스트림 값 읽기 189 | 2. 요청 라인의 파싱 190 | 3. 헤더의 파싱 191 | 4. 쿠키의 파싱 192 | 5. 파라미터 얻기 193 | 194 |
195 | 196 | **1. 소켓의 입력스트림 값 읽기** 197 | 198 | - 메소드, URI, HTTP 버전 정보를 담고 있는 요청 라인은 `java.io.InputStream` 클래스의 read 메소드를 써서 얻을 수 있다. 199 | 200 | - readRequestLine과 readHeader 메소드를 사용하기 위해 `SocketInputStream`을 사용한다. 201 | 202 | - `SocketInputStream` 인스턴스는 `InputStream` 객체와 사용할 버퍼 크기를 나타내는 정수 값을 전달해 생성한다. 203 | 204 |
205 | 206 | **2. 요청 라인의 파싱** 207 | 208 | - `HttpProcessor`의 process 메소드는 HTTP 요청의 첫 줄의 내용을 파싱하려는 목적으로 parseRequest 메소드를 호출한다. 209 | 210 | - parseRequest 메소드가 요청 라인의 HTTP 메소드, URI, 프로토콜 정보, 질의문, jsessionid을 추출한다. 211 | 212 | - parseReqeust 메소드는 uri를 normalize 메소드에 넘겨 비정상적인 URI를 바로 잡는다. 예를 들어 역슬래시(\\)는 모두 슬래시(/)로 바뀐다. 213 | 214 | ```java 215 | 216 | ``` 217 | 218 |
219 | 220 | **3. 헤더의 파싱** 221 | 222 | - HTTP 헤더는 `HttpHeader` 클래스로 나타낸다. `HttpHeader` 인스턴스는 파라미터가 없는 디폴트 생성자를 호출함으로써 생성 가능하다. 223 | 224 | - `HttpHeader` 인스턴스는 `SocketInputStream`의 readHeader 메소드로 전달할 수 있다. 225 | 226 |
227 | 228 | **4. 쿠키의 파싱** 229 | 230 | - 쿠키는 브라우저가 HTTP 요청 헤더로서 서버로 전송한다. 이 헤더명은 "Cookie"이며, 그 값은 둘 이상의 쿠키의 이름/값 쌍이 된다. 231 | 232 |
233 | 234 | **5. 파라미터 얻기** 235 | 236 | - `HttpRequest` 클래스에 있는 파라미터 메소드의 구현은 항상 자신의 parseParameters 메소드를 먼저 호출함으로써 시작된다. 237 | 238 | - 서블릿 프로그래머는 파라미터 값을 변경할 수 없어야 한다. 이러한 이유로 파라미터는 일반적인 `HashMap` 대신, `org.apache.catalina.util.ParameterMap`이라는 특별한 HashMap에 저장된다. 239 | 240 | > `ParameterMap` 클래스는 `java.util.HashMap` 을 확장하고, locked라는 boolean 타입의 필드를 갖고 있다. 파라미터 이름/값 쌍을 추가, 갱신, 제거하려면 locked의 값은 반드시 false이어야 한다. locked의 값이 true일 때 그와 같은 조작을 시도하면 `IllegalStateException`이 던져진다. 그러나 파라미터 값을 읽는 것은 언제든지 가능하다. 241 | 242 |
243 | 244 | #### HttpResponse 객체 생성허긔 245 | 246 | - `HttpResponse` 클래스는 `javax.servlet.http.HttpServletResponse` 인터페이스를 구현한 것이다. 247 | 248 | 249 | ```java 250 | public PrintWriter getWriter() throws IOException { 251 | ResponseStream newStream = new ResponseStream(this); 252 | newStream.setCommit(false); 253 | 254 | // OutputStreamWriter를 사용하면 지정한 문자셋을 사용해 문자를 바이트로 인코딩하는 것이 가능하다 255 | OutputStreamWriter osr = new OutputStreamWriter(newStream, getCharacterEncoding()); 256 | 257 | // ResponseWriter 클래스는 모든 print와 println 메소드를 재정의함으로써, 이들 메소드를 호출할 때 자동으로 OutputStream에 내용을 쓸 수 있게 한다. 258 | writer = new ResponseWriter(osr); 259 | 260 | return writer; 261 | } 262 | ``` 263 | 264 |
265 | 266 | >>>>>> 톰캣 최종분석 요약 글입니다. 267 | -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap03/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap03/img01.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap04/README.md: -------------------------------------------------------------------------------- 1 | # 4장 톰캣의 기본 커넥터 2 | ### 작성자 3 | 4 | **추연훈** 5 | 6 | ## 목표 7 | 8 | - 기본 커넥터는 몇 가지 최적화 기법을 사용 9 | - 다양한 객체를 위한 풀을 제공함으로써 과도한 객체 생성을 방지 10 | - 코드 내의 다양한 곳에서 문자열(String) 대신 문자의 배열(array)을 사용 11 | - HTTP1.1의 새로운 특징 12 | - `org.apache.catalina.Connector` 인터페이스 설명 13 | - `HttpConnector`나 `HttpProcessor`의 발전된 형태 구현 14 | 15 |
16 | 17 | ## HTTP 1.1의 새로운 특징 18 | ####1. 지속 연결 19 | 20 | - 지속 연결을 사용하면 서버는 페이지가 다운로드 되자마자 연결을 닫는 것이 아니라, **웹 클라이언트가 페이지에서 필요로 하는 다른 자원들을 요청할 때까지 기다린다.** 21 | 22 | - HTTP 연결을 맺고 끊는 것이 상당히 부담이 되는 작업이므로 이 매커니즘은 웹서버, 클라이언트, 네트워크 부하를 모두 줄일 수 있다. 23 | 24 |
25 | 26 | ####2. 청크 인코딩 27 | 28 | - 지속 연결이 가능해짐으로써 서버는 여러 자원에 대한 바이트 스트림을 전송할 수 있게 됐으며, 클라이언트는 동일한 연결을 사용해 다중 요청을 할 수 있게 됐다. 29 | => 미리 컨텐츠 길이는 알 수 없더라도, 바이트를 해석하는 방법을 수신받는 측에 알릴 수 있는 별도의 수단이 필요 30 | 31 | - HTTP 1.1에서는 transfer-encoding이라는 특별한 헤더가 추가됐는데, 이 헤더는 **바이트 스트림이 청크(chunk) 단위로 전송될 것임을 가리킨다.** 32 | 33 |
34 | 35 | ####3. 100 (continue) 상태 코드 사용 36 | - HTTP 1.1 클라이언트는 Expect: 100-continue 헤더를 전송해, 요청 본체를 전송하기에 앞서 서버측의 승인을 기다릴 수 있다. 37 | - **클라이언트가 전송하고자 하는 요청 본체의 양이 많을 경우, 서버가 이를 받아들일 수 있는지 먼저 확인하고자 할 때 사용된다.** 38 | 39 |
40 | 41 | ## Connector 인터페이스 42 | 43 | - setContainer: 커넥터와 컨테이너를 연결할 때 사용 44 | - getContainer: 이 커넥터와 연결된 컨테이너를 리턴 45 | - creatRequest: 들어오는 HTTP 요청에 대한 요청 객체 생성 46 | - createResponse: 응답 객체 생성 47 | 48 |
49 | 50 | ![img1](./img01.jpg) 51 | 52 | ## HttpConnector 클래스 53 | ####1. 서버 소켓의 생성 54 | 55 | - `HttpConnector`의 initialize 메소드는 자신의 open 메소드를 호출해 `ServerSocket` 인스턴스를 얻는다. 56 | - open메소드에서는 `ServerSocket`의 생성자를 호출하는 것이 아니라 `ServerSocketFactory`로부터 `ServerSocket`을 얻는다. (디자인 패턴 - 팩토리 메소드 패턴) 57 | 58 |
59 | 60 | > HttpConnector의 initialize 메소드와 open 메소드 61 | 62 | ```java 63 | public void initialize() 64 | throws LifecycleException { 65 | if (initialized) 66 | throw new LifecycleException ( 67 | sm.getString("httpConnector.alreadyInitialized")); 68 | 69 | this.initialized=true; 70 | Exception eRethrow = null; 71 | 72 | try { 73 | //자신의 open메소드 호출 74 | serverSocket = open(); 75 | } catch (IOException ioe) { 76 | .................. 생략 77 | 78 | } 79 | 80 | //ServerSocketFactory에서 ServerSocket 인스턴스를 얻는다. 81 | private ServerSocket open() 82 | throws IOException, KeyStoreException, NoSuchAlgorithmException, 83 | CertificateException, UnrecoverableKeyException, 84 | KeyManagementException 85 | { 86 | 87 | // ServerSocketFactory 호출 88 | ServerSocketFactory factory = getFactory(); 89 | 90 | // 주소가 없으면 전체 주소와 연결한다. 91 | if (address == null) { 92 | log(sm.getString("httpConnector.allAddresses")); 93 | try { 94 | //serverSocket을 리턴값으로 준다. 95 | return (factory.createSocket(port, acceptCount)); 96 | } catch (BindException be) { 97 | throw new BindException(be.getMessage() + ":" + port); 98 | } 99 | } 100 | 101 | // 특정 주소에만 ServerSocket을 연결한다. 102 | try { 103 | InetAddress is = InetAddress.getByName(address); 104 | log(sm.getString("httpConnector.anAddress", address)); 105 | try { 106 | //serverSocket을 리턴값으로 준다. 107 | return (factory.createSocket(port, acceptCount, is)); 108 | } catch (BindException be) { 109 | throw new BindException(be.getMessage() + ":" + address + 110 | ":" + port); 111 | } 112 | } catch (Exception e) { 113 | log(sm.getString("httpConnector.noAddress", address)); 114 | try { 115 | return (factory.createSocket(port, acceptCount)); 116 | } catch (BindException be) { 117 | throw new BindException(be.getMessage() + ":" + port); 118 | } 119 | } 120 | 121 | } 122 | ``` 123 | 124 |
125 | 126 | ####2. `HttpProcessor` 인스턴스의 관리 127 | 128 | - `HttpConnector`는 `HttpProcessor` 객체의 풀을 가지며, 각 `HttpProcessor` 인스턴스는 자신의 스레드를 가지기 때문에 `HttpConnector`는 동시에 여러 HTTP 요청을 처리할 수 있게 된다. 129 | 130 | - `HttpProcessor` 인스턴스는 HTTP 요청 라인과 헤더를 파싱하고 요청 객체를 만들어야 하는 책임이 있다. 131 | 132 | - 각 `HttpProcessor`는 요청/응답 객체와 연관돼있으므로, `HttpProcessor`의 생성자 메소드 내에서 `HttpConnector` 클래스의 creatRequest와 createResponse 메소드를 호출한다. 133 | 134 |
135 | 136 | ####3. HTTP 요청의 공급 137 | 138 | - HTTP 요청이 들어오면 createProcessor 메소드로 `HttpProcessor` 인스턴스를 얻는다. 그러나 `HttpProcessor`가 최대 개수에 도달하면 소켓을 닫아서 들어오는 HTTP 요청을 처리하지 않게 되고 createProcessor는 null을 리턴한다. 139 | 140 | - chap04장부터는 소켓의 입력 스트림을 읽어 HTTP 요청을 파싱하는 일은 `HttpProcessor`의 몫이 된다. 141 | 142 | - assign 메소드는 `HttpProcessor`가 파싱을 완료할 때까지 기다리는 것이 아니라, 호출된 후에 바로 리턴함으로써 그 다음 들어오는 HTTP 요청을 곧바로 처리할 수 있게 한다. 143 | 144 |
145 | 146 | ## `HttpProcessor` 클래스 147 | 148 | - `HttpProcessor`에서 run메소드의 while 루프는 await 메소드를 호출하는 부분에서 정지된다. await 메소드는 `HttpConnector`로부터 새로운 소켓을 얻을 때까지 '프로세서 스레드'의 흐름을 보류시킨다. 149 | 150 | 151 | ```java 152 | //HttpProcessor의 await 메소드와 assign 메소드 153 | 154 | // HttpProcessor의 run메소드 안에서 실행된다 155 | //소켓이 들어오면 소켓을 리턴해주는 역할을 한다 156 | private synchronized Socket await() { 157 | 158 | // 새로운 소켓을 얻을 때까지 프로세서 스레드 흐름을 보류시킨다 159 | // 처음 available 값은 false 이다. 160 | while (!available) { 161 | try { 162 | // 프로세서 스레드가 일시정지 된다 163 | // 새로운 소켓이 들어오면 assign 메소드에서 스레드를 깨워준다 164 | wait(); 165 | } catch (InterruptedException e) { 166 | } 167 | } 168 | 169 | // 새로운 소켓을 할당받으면 지역 socket 변수에 할당하여 리턴한다. 170 | Socket socket = this.socket; 171 | 172 | // false로 바꿔 assign 메소드가 정상 동작하도록 한다. 173 | available = false; 174 | 175 | // 커넥터 스레드는 assign 메소드의 while 루프 내에 정지해 있어 스레드를 깨워준다. 176 | notifyAll(); 177 | 178 | if ((debug >= 1) && (socket != null)) 179 | log(" The incoming request has been awaited"); 180 | 181 | // 지역 socket 변수를 리턴하는 이유는 현재의 소켓이 처리되기 전이라 할지라도, 182 | // 인스턴스 socket 변수는 다음에 들어오는 소켓을 곧바로 할당 받을 수 있어야하기 때문이다. 183 | return (socket); 184 | 185 | } 186 | 187 | // 소켓이 들어오면 동작하는 메소드이다 188 | synchronized void assign(Socket socket) { 189 | 190 | while (available) { 191 | try { 192 | // 커넥터 스레드가 정지한다. 193 | // await 메소드에서 커넥터 스레드를 깨워준다. 194 | wait(); 195 | } catch (InterruptedException e) { 196 | } 197 | } 198 | 199 | // 소켓을 지역 socket 변수에 할당한다. 200 | this.socket = socket; 201 | 202 | // true로 바꿔주면 await 메소드 루프에서 빠져나온다. 203 | available = true; 204 | 205 | // 프로세서 스레드는 다시 깨어난다. 이때 await 메소드에 알리는 것이다. 206 | notifyAll(); 207 | 208 | if ((debug >= 1) && (socket != null)) 209 | log(" An incoming request is being assigned"); 210 | 211 | } 212 | 213 | ``` 214 | 215 |
216 | 217 | ## 요청 처리 218 | 219 | - `HttpProcessor` 클래스의 process 메소드의 역할 220 | 221 | 1. 연결 정보의 파싱 222 | 2. 요청의 파싱 223 | 3. 헤더의 파싱 224 | 225 | ####1. 연결 정보의 파싱 226 | 227 | - parseConnection 메소드는 소켓에서 인터넷 주소값을 얻어 `HttpRequestImpl` 객체에 입력한다 228 | - 프락시가 사용되고 있는지도 확인해 소켓을 요청 객체에 넘긴다. 229 | 230 | ```java 231 | // HttpProcessor 클래스의 parseConnection메소드 232 | 233 | private void parseConnection(Socket socket) 234 | throws IOException, ServletException { 235 | 236 | if (debug >= 2) 237 | log(" parseConnection: address=" + socket.getInetAddress() + 238 | ", port=" + connector.getPort()); 239 | 240 | // 인터넷 주소값 추출 241 | ((HttpRequestImpl) request).setInet(socket.getInetAddress()); 242 | 243 | // 프록시 확인 244 | if (proxyPort != 0) 245 | request.setServerPort(proxyPort); 246 | else 247 | request.setServerPort(serverPort); 248 | request.setSocket(socket); 249 | 250 | } 251 | ``` 252 | 253 |
254 | 255 | ####2. 요청의 파싱 256 | 257 | - parseReqeust 메소드에서 요청 라인을 파싱해 다양한 값을 얻어내며, 이 값을 `HttpRequest` 객체에 할당한다. 258 | 259 | - parseRequest 메소드가 요청 라인의 HTTP 메소드, URI, 프로토콜 정보, 질의문, jsessionid을 추출한다. 260 | 261 | - parseReqeust 메소드는 uri를 normalize 메소드에 넘겨 비정상적인 URI를 바로 잡는다. 예를 들어 역슬래시(\\)는 모두 슬래시(/)로 바뀐다. 262 | 263 |
264 | 265 | ####3. 헤더의 파싱 266 | 267 | - 헤더의 파싱은 parseHeaders 메소드에서 수행되며 `HttpHeader` 클래스와 `DefaultHeaders` 클래스를 사용한다. 268 | - `HttpHeader` 클래스는 HTTP 요청헤더를 나타내며 문자열 대신 문자의 배열을 사용함으로써 String 객체의 과도한 연산을 피하고 있다. 269 | - 더이상 읽을 헤더가 없을 때까지 HTTP 요청을 읽어 들이는 while 루프가 있으며 헤더 값을 추출한다. 270 | - 마지막으로 parseHeaders 메소드는 헤더의 이름과 `DefaultHeaders`에 정의된 표준 이름을 비교하며, 문자열이 아니라 2개의 문자 배열을 비교한다. 271 | 272 | 273 | 274 | 275 |
276 | 277 | >>>>>> 톰캣 최종분석 요약 글입니다. 278 | -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap04/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap04/img01.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap05/README.md: -------------------------------------------------------------------------------- 1 | # 5장 컨테이너 2 | ### 작성자 3 | 4 | **추연훈** 5 | 6 | ## 목표 7 | 8 | - 컨텍스트(context)와 래퍼(wrapper)를 설명한다. 9 | - `Container` 인터페이스를 살펴본 후, 컨테이너의 파이프라인 매커니즘을 알아본다. 10 | 11 |
12 | 13 | ## `Container` 인터페이스 14 | 15 | - 컨테이너는 반드시 `org.apache.catalina.Container`를 구현해야 한다. 컨테이너 인스턴스는 커넥터의 setCotainer 메소드에 전달돼 커넥터가 컨테이너의 invoke 메소드를 호출하게 한다. 16 | 17 | - 카탈리나 컨테이너는 각기 다른 개념적 단계에 따라 네 종류의 컨테이너가 존재한다. 18 | - **엔진(Engine)은 카탈리나 서블릿 엔진 전체를 나타낸다.** 19 | - **호스트(Host)는 다양한 컨텍스트에 따른 가상 호스트를 나타낸다.** 20 | - **컨텍스트(Context)는 웹 어플리케이션을 나타내며 둘 이상의 래퍼를 가진다.** 21 | - **래퍼(Wrapper)는 각각의 서블릿을 나타낸다.** 22 | 23 | - 위에서 말한 각각의 개념적인 단계는 `org.apache.catalina` 패키지의 Engine, Host, Context, Wrapper 인터페이스로 표현되고 이들은 모두 `Container` 인터페이스를 확장한다. 24 | 25 | - 컨테이너에는 하위 단계의 컨테이너가 없을 수도 있고 반대로 하위 컨테이너가 많이 있을 수도 있다. 컨테이너 하나에 하위 컨테이너를 추가할 때는 `Container` 인터페이스의 addChild 메소드를 사용하면 된다. 반대로 컨테이너로부터 하위 컨테이너를 제거하려면 removeChild 메소드를 사용하면 된다. 26 | 27 | - `Container` 인터페이스에서 각각의 하위 컨테이너나 하위 컨테이너의 집합을 찾으려면 findChild와 findChildren 메소드를 사용하면 된다. 28 | 29 | ![img1](./img01.jpg) 30 | 31 |
32 | 33 | ## 파이프라인 태스크 34 | 35 | - 파이프라인은 컨테이너가 호출할 태스크들을 포함하여 **하나의 특정한 태스크는 밸브 하나로 표현된다.** 밸브는 전달받은 요청/응답 객체를 조작할 수 있다. 36 | 37 | - **하나의 컨테이너는 하나의 파이프라인을 가질 수 있다.** 컨테이너의 invoke 메소드가 호출되면 컨테이너는 파이프라인으로 처리권을 넘기며, 파이프라인은 첫 번째 밸브를 호출한다. invoke 메소드는 컨테이너가 파이프라인 내의 모든 밸브를 호출할 때 처음 호출하는 메소드이다. 38 | 39 | ![img4](./img04.jpg) 40 | 41 |
42 | 43 | #### `Pipeline` 인터페이스 44 | 45 | - `Pipeline` 인터페이스는 새로운 밸브를 추가하거나 기존의 밸브를 제거할 수 있도록, 각각의 addValve와 removeValve라는 메소드를 제공한다. 46 | 47 |
48 | 49 | #### `Valve` 인터페이스 50 | 51 | - 밸브는 요청의 처리에 대한 책임이 있는 컴포넌트이며 `Valve` 인터페이스는 하나의 밸브를 나타낸다. 52 | 53 | ```java 54 | // 밸브는 요청의 처리에 대한 책임이 있는 컴포넌트이다. 55 | 56 | public interface Valve { 57 | 58 | // 밸브가 구현한 객체에 관한 정보를 리턴하는 메소드 59 | public String getInfo(); 60 | 61 | // 컨테이너가 파이프라인 내의 밸브를 호출할 때 처음 호출하는 메소드 62 | public void invoke(Request request, Response response, 63 | ValveContext context) 64 | throws IOException, ServletException; 65 | 66 | 67 | } 68 | ``` 69 | 70 |
71 | 72 | ## `Wrapper` 인터페이스 73 | 74 | - **래퍼는 개별적인 하나의 서블릿을 대변하는 컨테이너**이다. `Wrapper`의 구현 클래스는 각각의 **서블릿의 생명주기**를 책임지며, 래퍼는 가장 하위 단계의 컨테이너이므로 래퍼에 또 다시 하위 컨테이너를 추가할 수 없다. 75 | 76 | - `Wrapper` 인터페이스의 중요한 메소드는 allocate와 load이며, allocate 메소드는 초기 서블릿 인스턴스를 래퍼에 할당하고 load 메소드는 래퍼가 대변하는 서블릿 인스턴스를 로드하고 초기화한다. 77 | 78 |
79 | 80 | ## `Context` 인터페이스 81 | 82 | - **컨텍스트는 웹 어플리케이션을 나타내는 컨테이너**이다. 컨텍스트는 하위 컨테이너로서 보통 둘 이상의 래퍼를 갖고 있다. 83 | 84 |
85 | 86 | ## 래퍼 어플리케이션 = 가장 하위 단계의 컨테이너 87 | 88 | - 다음의 래퍼 어플리케이션은 가장 작은 컨테이너 모듈을 작성하는 방법을 보여준다. 89 | 90 | ![img2](./img02.jpg) 91 | 92 | 1) `SimpleLoader`: **컨테이너 내에서 서블릿 클래스를 로드하는 임무를 가졌다.** 서블릿 클래스들이 위치하는 곳을 알고 있으며 getClassLoader 메소드를 통해 java.lang.ClassLoader 인스턴스를 리턴하는데, 이 `ClassLoader` 인스턴스가 서블릿 클래스의 위치에서 해당 서블릿을 찾는다. 93 | 94 | 2) `SimplePipeline`: **컨테이너가 호출할 태스크들을 포함하여 하나의 특정한 태스크는 밸브 하나로 표현한다.** 밸브는 전달받은 요청/응답 객체를 조작할 수 있다. 95 | 96 | 3) `SimpleWrapper`: **서블릿 생명주기와 관련한 역할을 하는 클래스**이다. allocate와 load 메소드를 구현한다. allocate 메소드는 초기 서블릿 인스턴스를 래퍼에 할당하고 load 메소드는 래퍼가 대변하는 서블릿 인스턴스를 로드하고 초기화한다. 97 | 98 | 4) `SimpleWrapperValve`: 이 클래스는 **SimpleWrapper 클래스를 위해 요청을 처리하는 기본 밸브**이다. invoke 메소드는 래퍼가 표현하고 있는 서블릿 인스턴스를 얻으려는 목적으로 SimpleWrapper 클래스의 allocate 메소드를 호출한다. 기본 밸브는 래퍼 자체가 아닌 서블릿의 service 메소드를 직접 호출하는 것이다. 99 | 100 | 5) `ClientIPLogeerValve`: **클라이언트의 IP 주소를 콘솔에 출력하는 밸브이다.** invoke 메소드에서 처음 하는 일은 밸브 컨텍스트의 invokeNext 메소드를 사용해 파이프라인의 다음 밸브를 호출한다. 그 다음 요청 객체의 getRemoteAddr 메소드로부터 얻은 값을 포함해 몇 줄의 문자열을 출력한다. 101 | 102 | 6) `HeaderLoggerValve`: **요청 헤더의 값을 콘솔에 출력하는 밸브이다.** invoke 메소드에서 밸브 컨텍스트의 invokeNext 메소드를 사용해 파이프라인의 다음 밸브를 호출하며, 그 다음 몇가지 요청 헤더의 값을 출력한다. 103 | 104 | 7) `Bootstrap1`: 애플리케이션을 시작하는 데 사용된다. 105 | 106 |
107 | 108 | ## 컨텍스트 어플리케이션 = 래퍼보다 상위 컨테이너 109 | 110 | - 대부분의 웹 어플리케이션은 **다수의 서블릿**을 갖는데 이 경우 래퍼와는 다른 종류의 컨테이너인 컨텍스트가 필요하다. 111 | 112 | - **둘 이상의 래퍼를 가지려면 맵퍼(mapper)라는 것이 필요**한데, 맵퍼는 컨테이너가 특정한 요청을 처리할 하위 컨테이너를 선택하는 것을 도와주는 컴포넌트이다. 113 | 114 | > 맵퍼는 톰캣 4에서만 존재한다. 톰캣 5는 하위 컨테이너를 찾을 때 다른 방법을 사용한다. 115 | 116 | - 하나의 컨테이너는 다중 프로토콜을 지원하기 위해 다수의 맵퍼를 사용할 수 있다. 117 | 118 | ![img3](./img03.jpg) 119 | 120 | 처리 순서를 정리하자면... 121 | 122 | 0. HTTP 요청이 들어오면 커넥터는 컨텍스트의 invoke 메소드를 호출한다. 123 | 124 | 1. 하나의 컨테이너는 하나의 파이프라인을 갖는다. **컨테이너의 invoke 메소드는 파이프라인의 invoke 메소드를 호출한다.** 125 | 126 | 2. **파이프라인의 invoke 메소드는 컨테이너에 추가된 모든 밸브를 호출한 다음 기본 밸브의 invoke 메소드를 호출한다.** 127 | 128 | 3. **래퍼의 기본 밸브는 관련된 서블릿 클래스를 로드하고 요청에 대한 응답을 할 책임을 진다.** 129 | 130 | 4. 컨텍스트의 기본 밸브는 요청을 처리하는 책임을 가지는 하위 컨테이너에 찾기 위해 맵퍼를 이용한다. **하위 컨테이너를 찾은 후에는 그 컨테이너의 invoke 메소드를 호출한다. 그 다음 1단계로 돌아간다.** 131 | 132 |
133 | 134 | #### `SimpleContext` 클래스 135 | 136 | - `SimpleContext` 클래스는 **커넥터에 할당되는 주된 컨테이너**이다. `SimpleContext`가 각각의 요청에 대해 어떤 래퍼를 호출해야 하는지 결정할 수 있게 하려면 요청 URL 패턴을 래퍼의 이름과 맵핑해야 한다. 137 | 138 | - 매핑과 관련된 메소드는 다음과 같다. 139 | - addServletMapping: URL 패턴/래퍼의 이름을 추가한다. 140 | - findServletMapping: 주어진 URL 패턴으로 래퍼의 이름을 얻어온다. 141 | - addMapper: 컨텍스트에 맵퍼를 추가한다 142 | - findMapper: 원하는 맵퍼를 찾을 때 사용한다. 143 | - map: 해당 요청을 처리할 책임이 있는 래퍼를 리턴한다. 144 | 145 |
146 | 147 | #### `Bootstrap2` 클래스 148 | 149 | - `Bootstrap1` 클래스와 거의 비슷하지만 2개의 래퍼를 생성하고 컨텍스트에 로더가 추가된다. 150 | 151 |
152 | 153 | ## 요약 154 | 155 | - 컨테이너는 **로더, 로거, 매니저**와 같은 여러 다른 모듈들을 사용한다. 156 | - 컨테이너의 종류로는 **엔진, 호스트, 컨텍스트, 래퍼**가 있다. 157 | 158 |
159 | 160 | >>>>>> 톰캣 최종분석 요약 글입니다. 161 | -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap05/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap05/img01.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap05/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap05/img02.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap05/img03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap05/img03.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap05/img04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap05/img04.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap09/README.md: -------------------------------------------------------------------------------- 1 | # 9장 세션 관리 2 | ### 작성자 3 | 4 | **추연훈** 5 | 6 | ## 서론: 당신이 궁금한 세션에 대해서 7 | #### 세션이란 무엇일까? 8 | 9 | - 서버가 해당 서버로 접근한 **클라이언트(사용자)를 식별**하기 위해 서버에 저장하는 파일을 말한다. 10 | 11 | #### 세션을 사용하는 이유는? 12 | 13 | - **HTTP 통신은 통신이 끝나면 상태 정보를 유지하지 않는 특성이 있다.** 이 특징을 해결하기 위해 쿠키와 세션을 사용한다. 14 | - 세션은 쿠키를 기반으로 하지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 **세션은 서버 측에서 관리한다.** 15 | - **서버는 클라이언트를 구분하기 위해 세션ID를 부여**하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증상태를 유지한다. 16 | 17 | #### 세션의 동작 순서 18 | 19 | 1. 클라이언트가 서버로 접속(http)한다. 20 | 2. 서버는 접근한 클라이언트의 쿠키를 확인해 클라이언트가 해당 session-id를 보내왔는지 확인한다. 21 | 3. 클라이언트로부터 발송된 session-id가 없으면, 서버는 session-id를 생성해 클라이언트에게 응답하고 이 session-id를 서버에 저장한다. 22 | 4. 클라이언트가 브라우저를 닫거나 세션 Time 한계(기본 30분) 시 session-id은 소멸된다. 23 | 24 |
25 | 26 | ## 본론: 톰캣 내에서 일어나는 세션 관리 27 | 28 | - 카탈리나는 **매니저라는 컴포넌트를 통해 세션을 관리한다.** 29 | - **하나의 매니저는 하나의 컨텍스트와 항상 연결되어 있다.** 30 | - 매니저는 세션 객체를 생성, 갱신, 제거(무효화)하는 일 외에도 유효한 세션 객체를 요청한 컴포넌트에게 리턴해주는 역할도 한다. 31 | - **매니저는 기본적으로 세션 객체를 메모리에 저장한다.** 32 | > 톰캣은 매니저가 세션 객체를 파일이나 데이터베이스에 지속적으로 저장하는 것도 허용 33 | 34 |
35 | 36 | ## 세션 37 | 38 | ![img1](./img01.jpg) 39 | 40 | - 세션 객체는 `HttpSession` 인스턴스로 대변되고 구현 클래스는 `StandardSession` 클래스이다. 41 | - **보안상의 이유**로 매니저는 `StandardSession` 인스턴스를 서블릿에 전달하지 않고 대신에 `StandardSessionFacade` 라는 퍼사드 클래스를 사용한다. 42 | - 내부적으로 매니저는 `Session` 인터페이스와 함께 작동된다. 43 | 44 |
45 | 46 | #### `Session` 인터페이스 47 | 48 | - `Session` 인터페이스는 카탈리나의 내부 퍼사드로서 작동한다. 49 | 50 | > 퍼사드 패턴은 서브시스템 내부에 있는 클래스에 접근할 수 있는 하나의 통합된 인터페이스를 제공하는 패턴이다. 51 | 52 | - `Session` 객체는 언제나 매니저와 연결돼있으며, setManager와 getManager 메소드를 사용해 `Session` 객체를 매니저에 연결할 수 있다. 53 | - `Session` 객체는 매니저와 연결돼있는 **컨텍스트 내에서 유일한 식별자를 갖는다.** 54 | 55 |
56 | 57 | #### `StandardSession` 클래스 58 | 59 | - `Session` 인터페이스의 표준 구현 클래스이다. 60 | - 생성자 메소드에서 `Manager` 인스턴스를 받음으로써 `Session` 객체가 항상 `Manager`를 갖고 있게 한다. 61 | - `Manager`의 maxInactiveInterval 변수에 지정된 시간동안 한번도 접근된 적이 없는 `Session` 객체는 만료되어야 한다. 62 | 63 | > 세션이 만료가 되면 서버에서 저장하고 있던 사용자의 세션 정보를 삭제한다. 대부분의 웹 컨테이너의 기본값은 1800초(30분)이다. 64 | 65 | - `Session` 인터페이스의 expire 메소드를 호출함으로써 `Session` 객체를 만료시킬 수 있다. 66 | 67 |
68 | 69 | #### `StandardSessionFacade` 클래스 70 | 71 | - 카탈리나는 `StandardSessionFacade`의 인스턴스를 서블릿에 전달한다. 이렇게 함으로써 서블릿 프로그래머가 사용해서는 안되는 `StandardSession`의 메소드를 호출하는 것을 방지할 수 있다. 72 | 73 |
74 | 75 | ## 매니저 76 | 77 | - 매니저는 세션 객체를 관리한다. 78 | 79 | ![img2](./img02.jpg) 80 | 81 | - `ManagerBase` 클래스는 모든 매니저 클래스들이 확장해야 하는 **추상 클래스**이다. `MangerBase`는 **Session 객체를 생성하기 위한 createSession 메소드를 갖고 있다.** **모든 세션은 각각 유일한 식별자를 갖는데, `ManagerBase` 클래스의 generateSessionid 메소드는 해당 세션의 유일한 식별자를 리턴한다.** 82 | 83 | ```java 84 | // 세션ID 만드는 알고리즘 85 | 86 | protected synchronized String generateSessionId() { 87 | 88 | // 랜덤 값을 추출하여 byte 배열에 저장 89 | Random random = getRandom(); 90 | byte bytes[] = new byte[SESSION_ID_BYTES]; 91 | getRandom().nextBytes(bytes); 92 | bytes = getDigest().digest(bytes); 93 | 94 | // 16진법으로 전환 95 | StringBuffer result = new StringBuffer(); 96 | for (int i = 0; i < bytes.length; i++) { 97 | byte b1 = (byte) ((bytes[i] & 0xf0) >> 4); 98 | byte b2 = (byte) (bytes[i] & 0x0f); 99 | if (b1 < 10) 100 | result.append((char) ('0' + b1)); 101 | else 102 | result.append((char) ('A' + (b1 - 10))); 103 | if (b2 < 10) 104 | result.append((char) ('0' + b2)); 105 | else 106 | result.append((char) ('A' + (b2 - 10))); 107 | } 108 | return (result.toString()); 109 | 110 | } 111 | ``` 112 | 113 |
114 | 115 | #### `StandardManager` 클래스 116 | 117 | - `Manager` 인터페이스의 표준 구현 클래스로서, 기동시 **세션 객체를 메모리에 저장**한다. 그러나 중지될 때는 현재 메모리에 존재하는 모든 세션 객체를 파일로 저장한다. 다시 기동될 때는 파일로부터 세션 객체를 다시 로드한다. 118 | 119 | - processExpire 메소드는 루프를 돌면서 `StandardManager`가 관리하는 모든 `Session` 인스턴스의 lastAccessedTime의 값을 현재 시각과 비교하는 루프를 수행하여 maxInactiveInterval의 값을 초과하면 Session 인스턴스의 expire 메소드를 호출해 `Session`을 만료시킨다. 120 | 121 |
122 | 123 | #### `PersistentManagerBase` 클래스 124 | 125 | - 지속 매니저에서 세션 객체는 백업(back-up)될 수도 있고 스왑 아웃(swap-out)될 수도 있다. 126 | 127 | - **세션 객체가 백업될 때 세션 객체는 저장장치에 복사되며 원래의 세션 객체는 여전히 메모리에 남는다.** 따라서 서버에 이상이 생겼을 때는 활성 세션 객체를 저장장치로부터 다시 읽어올 수 있다. 128 | 129 | > 여기서 저장장치란 세션 객체가 저장될 보조기억장치를 말한다. 130 | 131 | - **세션 객체가 스왑 아웃될 때 세션 객체는 저장장치로 이동되는데 메모리 공간의 절약을 위해서이다.** 이는 세션 객체가 너무 오랫동안 사용되지 않았거나 활성 세션 객체의 수가 지정된 세션의 수를 초과했기 때문이다. 132 | 133 |
134 | 135 | **스왑 아웃** 136 | 137 | - `PersistentManagerBase` 클래스는 세션 객체를 스왑 아웃시키는 데 있어서 여러 가지 규칙을 적용한다. 138 | - 너무 많은 세션 객체가 존재할 경우 -> 단순히 아무 세션 객체나 스왑 아웃시키게 된다. 139 | - 세션 객체가 너무 오랫동안 사용되지 않은 경우 140 | 141 |
142 | 143 | **백업** 144 | 145 | - 모든 활성 세션 객체가 백업되는 것은 아니다. 146 | - maxIdleBackup의 값보다 더 오랫동안 사용되지 않은 세션 객체만을 백업한다. 147 | 148 |
149 | 150 | #### `DistributedManager` 클래스 151 | 152 | - `DistributedManager` 클래스는 둘 이상의 노드로 구성된 클러스터 환경에서 사용된다. 하나의 노드는 하나의 톰캣 서버를 말한다. 153 | 154 | - `DistributedManager`의 주된 역할은 **세션 복제**이다. 155 | 156 | > 세션 복제는 클러스터 환경에서 사용자의 세션을 보장하는 하나의 방법으로 세션 클러스터링(session clustering)의 한 종류이다. 이로 인해 사용자의 HTTP 요청은 어떤 노드(톰캣)에 전달되더라도 기존에 생성한 동일한 세션 정보를 사용할 수 있게 된다. 157 | 158 |
159 | 160 | ## `Store` 인터페이스 161 | 162 | - `Store` 인터페이스는 매니저에 의해 관리되는 세션들을 저장하기 위한 지속적인 저장장치를 제공하는 컴포넌트이다. 163 | 164 | ![img3](./img03.jpg) 165 | 166 | 1) `StoreBase`: 하위 클래스인 `FileStore`와 `JDBCStore`를 위해 공통 기능을 제공하는 **추상클래스**이다. 정기적으로 세션을 만료시킬지의 여부를 확인하고 **만료된 세션을 활성 세션을 집합으로부터 제거하는 역할을 맡는 별도의 스레드가 실행된다.** 167 | 2) `FileStore`: **세션 객체를 파일에 저장한다.** 파일이름은 세션 객체의 식별자와 동일하며 .session이라는 확장자가 붙는다. 168 | 3) `JDBCStore`: JDBC를 사용해 세션 객체를 데이터베이스에 저장한다. 169 | 170 |
171 | 172 | ## 참고 173 | 174 | https://interconnection.tistory.com/74 175 | 176 |
177 | 178 | >>>>>> 톰캣 최종분석 요약 글입니다. 179 | -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap09/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap09/img01.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap09/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap09/img02.jpg -------------------------------------------------------------------------------- /src/ChuYeonHoon/Chap09/img03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ChuYeonHoon/Chap09/img03.jpg -------------------------------------------------------------------------------- /src/ImSoobin/Chap01/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ImSoobin/Chap01/README.md -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/Constants.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.io.File; 4 | 5 | public class Constants { 6 | public static final String WEB_ROOT = 7 | System.getProperty("user.dir") + File.separator + "webroot"; 8 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/HttpServer1.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.net.Socket; 4 | import java.net.ServerSocket; 5 | import java.net.InetAddress; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.io.IOException; 9 | 10 | public class HttpServer1 11 | { 12 | /** WEB_ROOT는 HTML 또는 그 밖의 파일이 위치하는 디렉토리이다. 13 | * 이 애플리케이션에서의 WEB_ROOT는 작업 디렉토리 아래의 "webroot"라는 디렉토리이다. 14 | * 작업 디렉토리는 java 명령이 실행되는 현재의 위치를 말한다. 15 | */ 16 | // 종료명령 17 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; 18 | 19 | // 종료 명령을 받았는지 여부 20 | private boolean shutdown = false; 21 | 22 | 23 | public static void main(String[] args) 24 | { 25 | HttpServer1 server = new HttpServer1(); 26 | server.await(); 27 | } 28 | 29 | 30 | public void await() 31 | { 32 | ServerSocket serverSocket = null; 33 | int port = 8080; 34 | 35 | try 36 | { 37 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); 38 | } 39 | catch (IOException e) 40 | { 41 | e.printStackTrace(); 42 | System.exit(1); 43 | } 44 | 45 | // 요청을 기다리는 루프 46 | while (!shutdown) 47 | { 48 | Socket socket = null; 49 | InputStream input = null; 50 | OutputStream output = null; 51 | try 52 | { 53 | socket = serverSocket.accept(); 54 | input = socket.getInputStream(); 55 | output = socket.getOutputStream(); 56 | 57 | // Request 객체 생성 및 parse 호출 58 | Request request = new Request(input); 59 | request.parse(); 60 | 61 | // Response 객체 생성 62 | Response response = new Response(output); 63 | response.setRequest(request); 64 | 65 | // 서블릿에 대한 요청인지 정적 자원에 대한 요청인지 확인 66 | // 서블릿에 대한 요청은 "/servlet"으로 시작 67 | if (request.getUri().startsWith("/servlet/")) 68 | { 69 | ServletProcessor1 processor = new ServletProcessor1(); 70 | processor.process(request, response); 71 | } 72 | else 73 | { 74 | StaticResourceProcessor processor = new StaticResourceProcessor(); 75 | processor.process(request, response); 76 | } 77 | 78 | // 소켓 닫기 79 | socket.close(); 80 | // URL이 종료 명령이었는지 확인 81 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 82 | } 83 | catch (Exception e) 84 | { 85 | e.printStackTrace(); 86 | System.exit(1); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/HttpServer2.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.net.Socket; 4 | import java.net.ServerSocket; 5 | import java.net.InetAddress; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.io.IOException; 9 | 10 | public class HttpServer2 11 | { 12 | // 종료명령 13 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; 14 | 15 | // 종료 명령을 받았는지 여부 16 | private boolean shutdown = false; 17 | 18 | public static void main(String[] args) 19 | { 20 | HttpServer2 server = new HttpServer2(); 21 | server.await(); 22 | } 23 | 24 | public void await() 25 | { 26 | ServerSocket serverSocket = null; 27 | int port = 8080; 28 | 29 | try 30 | { 31 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); 32 | } 33 | catch (IOException e) 34 | { 35 | e.printStackTrace(); 36 | System.exit(1); 37 | } 38 | 39 | // 요청을 기다리는 루프 40 | while (!shutdown) 41 | { 42 | Socket socket = null; 43 | InputStream input = null; 44 | OutputStream output = null; 45 | 46 | try 47 | { 48 | socket = serverSocket.accept(); 49 | input = socket.getInputStream(); 50 | output = socket.getOutputStream(); 51 | 52 | // create Request object and parse 53 | Request request = new Request(input); 54 | request.parse(); 55 | 56 | // create Response object 57 | Response response = new Response(output); 58 | response.setRequest(request); 59 | 60 | // 서블릿에 대한 요청인지 정적 자원에 대한 요청인지 확인 61 | // 서블릿에 대한 요청은 "/servlet"으로 시작 62 | if (request.getUri().startsWith("/servlet/")) 63 | { 64 | ServletProcessor2 processor = new ServletProcessor2(); 65 | processor.process(request, response); 66 | } 67 | else { 68 | StaticResourceProcessor processor = new StaticResourceProcessor(); 69 | processor.process(request, response); 70 | } 71 | 72 | // 소켓 닫기 73 | socket.close(); 74 | // URL이 종료 명령이었는지 확인 75 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 76 | } 77 | catch (Exception e) { 78 | e.printStackTrace(); 79 | System.exit(1); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/PrimitiveServlet.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.*; 7 | public class PrimitiveServlet implements Servlet 8 | { 9 | public void init(ServletConfig config) throws ServletException 10 | { 11 | System.out.println("init"); 12 | } 13 | public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException 14 | { 15 | System.out.println("from service"); 16 | PrintWriter out = response.getWriter(); 17 | out.println("Hello. Roses are red."); 18 | out.print("Violets are blue"); 19 | } 20 | public void destroy() 21 | { 22 | System.out.println("destroy"); 23 | } 24 | public String getServletInfo() 25 | { 26 | return null; 27 | } 28 | public ServletConfig getServletConfig() 29 | { 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 2장 - 간단한 서블릿 컨테이너 3 | 4 | ### 작성자 5 | 6 | 임수빈 7 | 8 | 9 | ## 서블릿 컨테이너 10 | 11 | : 간단한 서블릿 뿐만아니라 정적 자원도 처리할 수 있다. 12 | 13 | 서블릿 프로그래밍은 javax.servlet, javax.servlet.http 패키지의 클래스/인터페이스를 통해 이뤄짐 14 | 15 | __* javax.servlet.Servlet은 가장 중요한 인터페이스__ 16 | 17 | **모든 서블릿은 이 인터페이스를 구현하거나 구현한 클래스를 확장해야함** 18 | 19 | ### javax.servlet.Servlet 인터페이스의 메소드 20 | 21 | - public void init (ServletConfig config) throws ServletException 22 | 23 | - public void service(ServletRequest request, ServletResponse response) 24 | 25 | - public void destroy() 26 | 27 | - public ServletConfig getServletConfig() 28 | 29 | - pulbic java.lang.String getServletInfo() 30 | - 31 | 32 | ## 서블릿 생명주기 관련 메소드 33 | __* init, service, destroy는 서블릿의 생명주기와 관련된 메소드__ 34 | 35 | ### 1. init 36 | 37 | 서블릿 클래스를 인스턴스화한 뒤, 서블릿 컨테이너가 호출함. 38 | 서블릿 컨테이너는 이 메소드를 __정확히 한번 호출__ 함으로써 39 | 40 | __해당 서블릿이 서비스를 할 수 있는 준비가 됐다는 것을 표시함.__ 41 | 42 | 서블릿이 어떤 요청을 받기 전에 성공적으로 끝나야함. 43 | 44 | DB드라이버나 어떤 초기값을 로드하는 등 오직 한번만 실행되는 코드만 재정의할 수 있음. 45 | (그렇지 않을 경우, 메소드는 대개 비워두는 것이 보통이다) 46 | 47 | ### 2. service 48 | 49 | ** 서블릿 컨테이너는 서블릿 요청이 있을 때마다 service메소드를 호출함. 이 때, 서블릿 컨테이너는 javax.servlet.ServletRequest 객체, javax.servlet.ServletResponse객체를 service메소드에 전달함. 50 | 51 | ServletRequest객체에는 클라이언트의 HTTP 요청에 관한 정보가 있으며, 52 | ServletResponse 객체는 서블릿의 응답에 관한 정보를 캡슐화함. 53 | 54 | service메소드는 서블릿의 생명주기 동안 자주 호출됨. 55 | 56 | ### 3. destroy 57 | 58 | 서블릿 컨테이너는 서비스 영역으로부터 서블릿 인스턴스를 제거하기 앞서 해당 서블릿 destroy메소드를 호출함. 59 | 서블릿 컨테이너가 종료되거나, 서블릿 컨테이너가 메모리를 확보해야 할 경우 발생함. 60 | 61 | ** 서블릿의 service메소드로부터 모든 스레드가 빠져나간 뒤나 제한시간이 지난 뒤에 호출됨. 62 | 63 | destory메소드를 호출하고 나면, 동일한 서블릿에 대해서 service메소드를 호출하지 않음. 64 | destory는 서블릿이 소유하고 있던 메모리, 파일 핸들, 스레드 등과 같은 자원을 깨끗이 반환할 수 있는 기회를 제공 65 | 66 | 모든 영속적인 상태가 메모리상의 현재 서블릿의 상태와 확실히 동기화할 수 있게끔 함. 67 | 68 | ## 서블릿의 HTTP요청 처리 방법 69 | 1. 어떤 서블릿을 처음으로 요청을 받았을 때, 해당 서블릿 클래스를 로드, 서블릿의 init메소드를 호출 70 | 2. 71 | 3. 각 요청에서 javax.servlet.ServletRequest, javax.servlet.ServletResponse인스턴스 생성 72 | 4. 73 | 5. ServletRequest, ServletResponse 객체 전달 -> 서블릿의 service메소드 호출 74 | 6. 75 | 7. 서블릿 클래스를 종료하면서 서블릿의 destroy 메소드 호출 76 | 77 | --- 78 | ### 간단한 서블릿컨테이너 (2장) 예제 코드의 하는 일 79 | 1. HTTP 요청을 기다림 80 | 81 | 2. 서블릿을 처음으로 요청받을때, 해당 서블릿 클래스 로드 -> 서블릿의 init메소드를 딱 한번 호출 82 | 83 | 3. 각 요청에서 javax.servlet.ServletRequest, javax.servlet.ServletResponse인스턴스 생성 84 | 85 | 4. ServletRequest, ServletResponse객체를 전달해 서블릿의 service메소드 호출 86 | 87 | 5. 서블릿 클래스를 종료하면서, 서블릿의 destroy메소드 호출, 서블릿 클래스 언로드 88 | 89 | 6. 서블릿 로드 90 | 91 | - 클래스 로드 생성 후 클래스 경로알려줌 92 | * 서블릿 로드 시 java.net.URLClassLoader클래스를 사용할 수 있음. 93 | 94 | 이 클래스는 java.lang.ClassLoader의 자식 클래스임. URLClassLoader의 인스턴스를 생성한 뒤에 로드할 수 있음. 95 | 96 | * 서블릿 컨테이너에서는 클래스 로더가 서블릿 클래스를 찾는 위치를 저장소라고 부름 97 | 98 | __** 2장에서의 서블릿 컨테이너는 서블릿이 요청을 받을 때마다 항상 서블릿 클래스를 로드한다.__ 99 | 100 | ![chap02 UML](./img/chap2.jpg) 101 | ### 코드예제 102 | ### 1. 애플리케이션1 103 | #### - HTTPServer1 104 | 105 | ```java 106 | import java.net.Socket; 107 | import java.net.ServerSocket; 108 | import java.net.InetAddress; 109 | import java.io.InputStream; 110 | import java.io.OutputStream; 111 | import java.io.IOException; 112 | 113 | public class HttpServer1 114 | { 115 | /** WEB_ROOT는 HTML 또는 그 밖의 파일이 위치하는 디렉토리이다. 116 | * 이 애플리케이션에서의 WEB_ROOT는 작업 디렉토리 아래의 "webroot"라는 디렉토리이다. 117 | * 작업 디렉토리는 java 명령이 실행되는 현재의 위치를 말한다. 118 | */ 119 | // 종료명령 120 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; 121 | 122 | // 종료 명령을 받았는지 여부 123 | private boolean shutdown = false; 124 | 125 | public static void main(String[] args) 126 | { 127 | HttpServer1 server = new HttpServer1(); 128 | server.await(); 129 | } 130 | 131 | public void await() 132 | { 133 | ServerSocket serverSocket = null; 134 | int port = 8080; 135 | 136 | try 137 | { 138 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); 139 | } 140 | catch (IOException e) 141 | { 142 | e.printStackTrace(); 143 | System.exit(1); 144 | } 145 | 146 | // 요청을 기다리는 루프 147 | while (!shutdown) 148 | { 149 | Socket socket = null; 150 | InputStream input = null; 151 | OutputStream output = null; 152 | try 153 | { 154 | socket = serverSocket.accept(); 155 | input = socket.getInputStream(); 156 | output = socket.getOutputStream(); 157 | 158 | // Request 객체 생성 및 parse 호출 159 | Request request = new Request(input); 160 | request.parse(); 161 | 162 | // Response 객체 생성 163 | Response response = new Response(output); 164 | response.setRequest(request); 165 | 166 | // 서블릿에 대한 요청인지 정적 자원에 대한 요청인지 확인 167 | // 서블릿에 대한 요청은 "/servlet"으로 시작 168 | if (request.getUri().startsWith("/servlet/")) 169 | { 170 | ServletProcessor1 processor = new ServletProcessor1(); 171 | processor.process(request, response); 172 | } 173 | else 174 | { 175 | StaticResourceProcessor processor = new StaticResourceProcessor(); 176 | processor.process(request, response); 177 | } 178 | 179 | // 소켓 닫기 180 | socket.close(); 181 | // URL이 종료 명령이었는지 확인 182 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 183 | } 184 | catch (Exception e) 185 | { 186 | e.printStackTrace(); 187 | System.exit(1); 188 | } 189 | } 190 | } 191 | } 192 | ``` 193 | 194 | #### - Request 195 | ```java 196 | import java.io.InputStream; 197 | import java.io.IOException; 198 | import java.io.BufferedReader; 199 | import java.io.UnsupportedEncodingException; 200 | import java.util.Enumeration; 201 | import java.util.Locale; 202 | import java.util.Map; 203 | import javax.servlet.RequestDispatcher; 204 | import javax.servlet.ServletInputStream; 205 | import javax.servlet.ServletRequest; 206 | 207 | 208 | public class Request implements ServletRequest 209 | { 210 | private InputStream input; 211 | private String uri; 212 | 213 | public Request(InputStream input) 214 | { 215 | this.input = input; 216 | } 217 | 218 | public String getUri() 219 | { 220 | return uri; 221 | } 222 | 223 | private String parseUri(String requestString) 224 | { 225 | int index1, index2; 226 | index1 = requestString.indexOf(' '); 227 | System.out.println("인덱스 테스트"+index1); 228 | if (index1 != -1) 229 | { 230 | index2 = requestString.indexOf(' ', index1 + 1); 231 | if (index2 > index1) 232 | { 233 | System.out.println("인덱스 테스트2"+requestString.substring(index1 + 1, index2)); 234 | return requestString.substring(index1 + 1, index2); 235 | } 236 | } 237 | return null; 238 | } 239 | 240 | public void parse() 241 | { 242 | // 소켓으로부터 일련의 문자들을 읽음 243 | StringBuffer request = new StringBuffer(2048); 244 | int i; 245 | byte[] buffer = new byte[2048]; 246 | try 247 | { 248 | i = input.read(buffer); 249 | } 250 | catch (IOException e) 251 | { 252 | e.printStackTrace(); 253 | i = -1; 254 | } 255 | for (int j=0; jFile Not Found"; 327 | output.write(errorMessage.getBytes()); 328 | } 329 | finally 330 | { 331 | if (fis!=null) 332 | fis.close(); 333 | } 334 | } 335 | 336 | /** implementation of ServletResponse */ 337 | ... 338 | ``` 339 | #### - StaticResourceProcessor 340 | ```java 341 | import java.io.IOException; 342 | 343 | public class StaticResourceProcessor 344 | { 345 | public void process(Request request, Response response) 346 | { 347 | try 348 | { 349 | response.sendStaticResource(); 350 | } 351 | catch (IOException e) 352 | { 353 | e.printStackTrace(); 354 | } 355 | } 356 | } 357 | ``` 358 | #### - ServletProcessor1 359 | ```java 360 | 361 | import java.net.URL; 362 | import java.net.URLClassLoader; 363 | import java.net.URLStreamHandler; 364 | import java.io.File; 365 | import java.io.IOException; 366 | import javax.servlet.Servlet; 367 | import javax.servlet.ServletRequest; 368 | import javax.servlet.ServletResponse; 369 | 370 | public class ServletProcessor1 371 | { 372 | public void process(Request request, Response response) 373 | { 374 | 375 | String uri = request.getUri(); 376 | String servletName = uri.substring(uri.lastIndexOf("/") + 1); 377 | System.out.println("서블릿이름은~?"); 378 | URLClassLoader loader = null; 379 | 380 | try 381 | { 382 | // URLClassLoader 생성 383 | URL[] urls = new URL[1]; 384 | URLStreamHandler streamHandler = null; 385 | File classPath = new File(Constants.WEB_ROOT); 386 | // 저장소를 만드는 부분은 387 | // org.apache.catalina.startup.ClassLoaderFactory의 addRepository메소드에서 사용 388 | String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ; 389 | // URL을 만드는 부분은 390 | // org.apache.catalina.loader.StandardClassLoader class 391 | urls[0] = new URL(null, repository, streamHandler); 392 | loader = new URLClassLoader(urls); 393 | } 394 | catch (IOException e) 395 | { 396 | System.out.println(e.toString()); 397 | } 398 | Class myClass = null; 399 | try 400 | { 401 | myClass = loader.loadClass(servletName); 402 | } 403 | catch (ClassNotFoundException e) 404 | { 405 | System.out.println(e.toString()); 406 | } 407 | 408 | Servlet servlet = null; 409 | 410 | try 411 | { 412 | servlet = (Servlet) myClass.newInstance(); 413 | servlet.service((ServletRequest) request, (ServletResponse) response); 414 | } 415 | catch (Exception e) 416 | { 417 | System.out.println(e.toString()); 418 | } 419 | catch (Throwable e) 420 | { 421 | System.out.println(e.toString()); 422 | } 423 | 424 | } 425 | } 426 | ``` 427 | 428 | ### 애플리케이션2 429 | 430 | ** 위의 애플리케이션1에는 심각한 문제 존재함. 431 | ServletProcessor1에서 process 메소드는 javax.servlet.ServletRequest로 업캐스팅해서 서블릿의 service메소드로 전달함. (Response도 동일) 432 | 433 | __이와 같은 방식은 보안 관련한 문제를 야기함__ 434 | 435 | 이 서블릿 컨테이너의 내부를 알고 있는 프로그래머라면, ServletRequest, ServletResponse를 각각 다운캐스팅해서 436 | public 메소드를 호출할 수 있을 것이다. 437 | Request 인스턴스로 parse메소드, Response 인스턴스로 sendStaticResource 메소드를 호출하는 것이 가능하다. 438 | 439 | ** 그렇다고 parse, sendStaticResource를 private로 만들 수 없음. 440 | (Httpserver에서 이들 메소드를 호출해야 하기 때문에) 441 | 442 | 두 메소드는 서블릿내에서 사용되는 것을 전제로 하지 않음. 443 | 444 | __해결방법__ 445 | 1. Request, Response클래스에 디폴트 접근 지정자를 적용하고 패키지 외부에서는 사용할 수 없게 하는 것 446 | 2. ** 퍼사드 클래스를 사용하는 것 447 | 448 | ```java 449 | import java.io.IOException; 450 | import java.io.BufferedReader; 451 | import java.io.UnsupportedEncodingException; 452 | import java.util.Enumeration; 453 | import java.util.Locale; 454 | import java.util.Map; 455 | import javax.servlet.RequestDispatcher; 456 | import javax.servlet.ServletInputStream; 457 | import javax.servlet.ServletRequest; 458 | 459 | public class RequestFacade implements ServletRequest 460 | { 461 | 462 | private ServletRequest request = null; 463 | 464 | public RequestFacade(Request request) 465 | { 466 | this.request = request; 467 | } 468 | /* implementation of the ServletRequest*/ 469 | public Object getAttribute(String attribute) 470 | { 471 | return request.getAttribute(attribute); 472 | } 473 | public Enumeration getAttributeNames() 474 | { 475 | return request.getAttributeNames(); 476 | } 477 | 478 | ... 479 | ``` 480 | ```java 481 | import java.io.IOException; 482 | import java.io.PrintWriter; 483 | import java.util.Locale; 484 | import javax.servlet.ServletResponse; 485 | import javax.servlet.ServletOutputStream; 486 | 487 | public class ResponseFacade implements ServletResponse { 488 | 489 | private ServletResponse response; 490 | 491 | public ResponseFacade(Response response) { 492 | this.response = response; 493 | } 494 | 495 | public void flushBuffer() throws IOException { 496 | response.flushBuffer(); 497 | } 498 | 499 | public int getBufferSize() { 500 | return response.getBufferSize(); 501 | } 502 | 503 | ... 504 | ``` 505 | [퍼사드 패턴](https://github.com/Soobinnn/Design-Pattern-Study/blob/master/src/facade/facade.md) 506 | 507 | : 일련의 복잡한 클래스들을 단순화 508 | 서로 긴밀하게 연결되지 않아도 되고. **최소 지식 원칙**(Principle of Least Knowledge)**** 을 준수하는데도 도움 509 | 510 | ** 여기서는 인터페이스의 단순화가 아닌, 클래스에 대한 접근 제어를 목적으로 이용 511 | 512 | 513 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/Request.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.io.InputStream; 4 | import java.io.IOException; 5 | import java.io.BufferedReader; 6 | import java.io.UnsupportedEncodingException; 7 | import java.util.Enumeration; 8 | import java.util.Locale; 9 | import java.util.Map; 10 | import javax.servlet.RequestDispatcher; 11 | import javax.servlet.ServletInputStream; 12 | import javax.servlet.ServletRequest; 13 | 14 | 15 | public class Request implements ServletRequest 16 | { 17 | private InputStream input; 18 | private String uri; 19 | 20 | public Request(InputStream input) 21 | { 22 | this.input = input; 23 | } 24 | 25 | public String getUri() 26 | { 27 | return uri; 28 | } 29 | 30 | private String parseUri(String requestString) 31 | { 32 | int index1, index2; 33 | index1 = requestString.indexOf(' '); 34 | System.out.println("인덱스 테스트"+index1); 35 | if (index1 != -1) 36 | { 37 | index2 = requestString.indexOf(' ', index1 + 1); 38 | if (index2 > index1) 39 | { 40 | System.out.println("인덱스 테스트2"+requestString.substring(index1 + 1, index2)); 41 | return requestString.substring(index1 + 1, index2); 42 | } 43 | } 44 | return null; 45 | } 46 | 47 | public void parse() 48 | { 49 | // 소켓으로부터 일련의 문자들을 읽음 50 | StringBuffer request = new StringBuffer(2048); 51 | int i; 52 | byte[] buffer = new byte[2048]; 53 | try 54 | { 55 | i = input.read(buffer); 56 | } 57 | catch (IOException e) 58 | { 59 | e.printStackTrace(); 60 | i = -1; 61 | } 62 | for (int j=0; jFile Not Found"; 61 | output.write(errorMessage.getBytes()); 62 | } 63 | finally 64 | { 65 | if (fis!=null) 66 | fis.close(); 67 | } 68 | } 69 | 70 | 71 | /** implementation of ServletResponse */ 72 | public void flushBuffer() throws IOException { 73 | } 74 | 75 | public int getBufferSize() { 76 | return 0; 77 | } 78 | 79 | public String getCharacterEncoding() { 80 | return null; 81 | } 82 | 83 | public Locale getLocale() { 84 | return null; 85 | } 86 | 87 | public ServletOutputStream getOutputStream() throws IOException { 88 | return null; 89 | } 90 | 91 | public PrintWriter getWriter() throws IOException { 92 | // autoflush is true, println() will flush, 93 | // but print() will not. 94 | writer = new PrintWriter(output, true); 95 | return writer; 96 | } 97 | 98 | public boolean isCommitted() { 99 | return false; 100 | } 101 | 102 | public void reset() { 103 | } 104 | 105 | public void resetBuffer() { 106 | } 107 | 108 | public void setBufferSize(int size) { 109 | } 110 | 111 | public void setContentLength(int length) { 112 | } 113 | 114 | public void setContentType(String type) { 115 | } 116 | 117 | public void setLocale(Locale locale) { 118 | } 119 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/ResponseFacade.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.util.Locale; 6 | import javax.servlet.ServletResponse; 7 | import javax.servlet.ServletOutputStream; 8 | 9 | public class ResponseFacade implements ServletResponse { 10 | 11 | private ServletResponse response; 12 | 13 | public ResponseFacade(Response response) { 14 | this.response = response; 15 | } 16 | 17 | public void flushBuffer() throws IOException { 18 | response.flushBuffer(); 19 | } 20 | 21 | public int getBufferSize() { 22 | return response.getBufferSize(); 23 | } 24 | 25 | public String getCharacterEncoding() { 26 | return response.getCharacterEncoding(); 27 | } 28 | 29 | public Locale getLocale() { 30 | return response.getLocale(); 31 | } 32 | 33 | public ServletOutputStream getOutputStream() throws IOException { 34 | return response.getOutputStream(); 35 | } 36 | 37 | public PrintWriter getWriter() throws IOException { 38 | return response.getWriter(); 39 | } 40 | 41 | public boolean isCommitted() { 42 | return response.isCommitted(); 43 | } 44 | 45 | public void reset() { 46 | response.reset(); 47 | } 48 | 49 | public void resetBuffer() { 50 | response.resetBuffer(); 51 | } 52 | 53 | public void setBufferSize(int size) { 54 | response.setBufferSize(size); 55 | } 56 | 57 | public void setContentLength(int length) { 58 | response.setContentLength(length); 59 | } 60 | 61 | public void setContentType(String type) { 62 | response.setContentType(type); 63 | } 64 | 65 | public void setLocale(Locale locale) { 66 | response.setLocale(locale); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/ServletProcessor1.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | import java.net.URLStreamHandler; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import javax.servlet.Servlet; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | 12 | public class ServletProcessor1 13 | { 14 | 15 | public void process(Request request, Response response) 16 | { 17 | 18 | String uri = request.getUri(); 19 | String servletName = uri.substring(uri.lastIndexOf("/") + 1); 20 | System.out.println("서블릿이름은~?"); 21 | URLClassLoader loader = null; 22 | 23 | try 24 | { 25 | // URLClassLoader 생성 26 | URL[] urls = new URL[1]; 27 | URLStreamHandler streamHandler = null; 28 | File classPath = new File(Constants.WEB_ROOT); 29 | // 저장소를 만드는 부분은 30 | // org.apache.catalina.startup.ClassLoaderFactory의 addRepository메소드에서 사용 31 | String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ; 32 | // URL을 만드는 부분은 33 | // org.apache.catalina.loader.StandardClassLoader class 34 | urls[0] = new URL(null, repository, streamHandler); 35 | loader = new URLClassLoader(urls); 36 | } 37 | catch (IOException e) 38 | { 39 | System.out.println(e.toString()); 40 | } 41 | Class myClass = null; 42 | try 43 | { 44 | myClass = loader.loadClass(servletName); 45 | } 46 | catch (ClassNotFoundException e) 47 | { 48 | System.out.println(e.toString()); 49 | } 50 | 51 | Servlet servlet = null; 52 | 53 | try 54 | { 55 | servlet = (Servlet) myClass.newInstance(); 56 | servlet.service((ServletRequest) request, (ServletResponse) response); 57 | } 58 | catch (Exception e) 59 | { 60 | System.out.println(e.toString()); 61 | } 62 | catch (Throwable e) 63 | { 64 | System.out.println(e.toString()); 65 | } 66 | 67 | } 68 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/ServletProcessor2.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | import java.net.URLStreamHandler; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import javax.servlet.Servlet; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | 12 | public class ServletProcessor2 13 | { 14 | 15 | public void process(Request request, Response response) 16 | { 17 | 18 | String uri = request.getUri(); 19 | String servletName = uri.substring(uri.lastIndexOf("/") + 1); 20 | URLClassLoader loader = null; 21 | 22 | try 23 | { 24 | // URLClassLoader 생성 25 | URL[] urls = new URL[1]; 26 | URLStreamHandler streamHandler = null; 27 | File classPath = new File(Constants.WEB_ROOT); 28 | // the forming of repository is taken from the createClassLoader method in 29 | // org.apache.catalina.startup.ClassLoaderFactory 30 | String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ; 31 | // the code for forming the URL is taken from the addRepository method in 32 | // org.apache.catalina.loader.StandardClassLoader class. 33 | urls[0] = new URL(null, repository, streamHandler); 34 | loader = new URLClassLoader(urls); 35 | } 36 | catch (IOException e) 37 | { 38 | System.out.println(e.toString() ); 39 | } 40 | Class myClass = null; 41 | try 42 | { 43 | myClass = loader.loadClass(servletName); 44 | } 45 | catch (ClassNotFoundException e) 46 | { 47 | System.out.println(e.toString()); 48 | } 49 | 50 | Servlet servlet = null; 51 | RequestFacade requestFacade = new RequestFacade(request); 52 | ResponseFacade responseFacade = new ResponseFacade(response); 53 | 54 | try 55 | { 56 | servlet = (Servlet) myClass.newInstance(); 57 | servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade); 58 | } 59 | catch (Exception e) 60 | { 61 | System.out.println(e.toString()); 62 | } 63 | catch (Throwable e) 64 | { 65 | System.out.println(e.toString()); 66 | } 67 | 68 | } 69 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/StaticResourceProcessor.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap02; 2 | 3 | import java.io.IOException; 4 | 5 | public class StaticResourceProcessor 6 | { 7 | 8 | public void process(Request request, Response response) 9 | { 10 | try 11 | { 12 | response.sendStaticResource(); 13 | } 14 | catch (IOException e) 15 | { 16 | e.printStackTrace(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap02/img/chap2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ImSoobin/Chap02/img/chap2.jpg -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ==================================================================== 3 | * 4 | * The Apache Software License, Version 1.1 5 | * 6 | * Copyright (c) 1999 The Apache Software Foundation. All rights 7 | * reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in 18 | * the documentation and/or other materials provided with the 19 | * distribution. 20 | * 21 | * 3. The end-user documentation included with the redistribution, if 22 | * any, must include the following acknowlegement: 23 | * "This product includes software developed by the 24 | * Apache Software Foundation (http://www.apache.org/)." 25 | * Alternately, this acknowlegement may appear in the software itself, 26 | * if and wherever such third-party acknowlegements normally appear. 27 | * 28 | * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software 29 | * Foundation" must not be used to endorse or promote products derived 30 | * from this software without prior written permission. For written 31 | * permission, please contact apache@apache.org. 32 | * 33 | * 5. Products derived from this software may not be called "Apache" 34 | * nor may "Apache" appear in their names without prior written 35 | * permission of the Apache Group. 36 | * 37 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 | * SUCH DAMAGE. 49 | * ==================================================================== 50 | * 51 | * This software consists of voluntary contributions made by many 52 | * individuals on behalf of the Apache Software Foundation. For more 53 | * information on the Apache Software Foundation, please see 54 | * . 55 | * 56 | * [Additional notices, if required by prior licensing conditions] 57 | * 58 | */ 59 | 60 | 61 | package ImSoobin.Chap07; 62 | 63 | /** 64 | * Manifest constants for the org.apache.catalina.logger 65 | * package. 66 | * 67 | * @author Craig R. McClanahan 68 | */ 69 | 70 | public class Constants { 71 | 72 | public static final String Package = "ImSoobin.Chap07"; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/FileLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/logger/FileLogger.java,v 1.8 2002/06/09 02:19:43 remm Exp $ 3 | * $Revision: 1.8 $ 4 | * $Date: 2002/06/09 02:19:43 $ 5 | * 6 | * ==================================================================== 7 | * 8 | * The Apache Software License, Version 1.1 9 | * 10 | * Copyright (c) 1999 The Apache Software Foundation. All rights 11 | * reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 17 | * 1. Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * 20 | * 2. Redistributions in binary form must reproduce the above copyright 21 | * notice, this list of conditions and the following disclaimer in 22 | * the documentation and/or other materials provided with the 23 | * distribution. 24 | * 25 | * 3. The end-user documentation included with the redistribution, if 26 | * any, must include the following acknowlegement: 27 | * "This product includes software developed by the 28 | * Apache Software Foundation (http://www.apache.org/)." 29 | * Alternately, this acknowlegement may appear in the software itself, 30 | * if and wherever such third-party acknowlegements normally appear. 31 | * 32 | * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software 33 | * Foundation" must not be used to endorse or promote products derived 34 | * from this software without prior written permission. For written 35 | * permission, please contact apache@apache.org. 36 | * 37 | * 5. Products derived from this software may not be called "Apache" 38 | * nor may "Apache" appear in their names without prior written 39 | * permission of the Apache Group. 40 | * 41 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 | * SUCH DAMAGE. 53 | * ==================================================================== 54 | * 55 | * This software consists of voluntary contributions made by many 56 | * individuals on behalf of the Apache Software Foundation. For more 57 | * information on the Apache Software Foundation, please see 58 | * . 59 | * 60 | * [Additional notices, if required by prior licensing conditions] 61 | * 62 | */ 63 | 64 | 65 | package ImSoobin.Chap07; 66 | 67 | 68 | import java.io.File; 69 | import java.io.FileWriter; 70 | import java.io.IOException; 71 | import java.io.PrintWriter; 72 | import java.sql.Timestamp; 73 | import org.apache.catalina.Lifecycle; 74 | import org.apache.catalina.LifecycleException; 75 | import org.apache.catalina.LifecycleListener; 76 | import org.apache.catalina.util.LifecycleSupport; 77 | import org.apache.catalina.util.StringManager; 78 | 79 | 80 | /** 81 | * Implementation of Logger that appends log messages to a file 82 | * named {prefix}.{date}.{suffix} in a configured directory, with an 83 | * optional preceding timestamp. 84 | * 85 | * @author Craig R. McClanahan 86 | * @version $Revision: 1.8 $ $Date: 2002/06/09 02:19:43 $ 87 | */ 88 | 89 | public class FileLogger 90 | extends LoggerBase 91 | implements Lifecycle { 92 | 93 | 94 | // ----------------------------------------------------- Instance Variables 95 | 96 | 97 | /** 98 | * The as-of date for the currently open log file, or a zero-length 99 | * string if there is no open log file. 100 | */ 101 | private String date = ""; 102 | 103 | 104 | /** 105 | * The directory in which log files are created. 106 | */ 107 | private String directory = "logs"; 108 | 109 | 110 | /** 111 | * The descriptive information about this implementation. 112 | */ 113 | protected static final String info = 114 | "org.apache.catalina.logger.FileLogger/1.0"; 115 | 116 | 117 | /** 118 | * The lifecycle event support for this component. 119 | */ 120 | protected LifecycleSupport lifecycle = new LifecycleSupport(this); 121 | 122 | 123 | /** 124 | * The prefix that is added to log file filenames. 125 | */ 126 | private String prefix = "catalina."; 127 | 128 | 129 | /** 130 | * The string manager for this package. 131 | */ 132 | private StringManager sm = 133 | StringManager.getManager(Constants.Package); 134 | 135 | 136 | /** 137 | * Has this component been started? 138 | */ 139 | private boolean started = false; 140 | 141 | 142 | /** 143 | * The suffix that is added to log file filenames. 144 | */ 145 | private String suffix = ".log"; 146 | 147 | 148 | /** 149 | * Should logged messages be date/time stamped? 150 | */ 151 | private boolean timestamp = false; 152 | 153 | 154 | /** 155 | * The PrintWriter to which we are currently logging, if any. 156 | */ 157 | private PrintWriter writer = null; 158 | 159 | 160 | // ------------------------------------------------------------- Properties 161 | 162 | 163 | /** 164 | * Return the directory in which we create log files. 165 | */ 166 | public String getDirectory() { 167 | 168 | return (directory); 169 | 170 | } 171 | 172 | 173 | /** 174 | * Set the directory in which we create log files. 175 | * 176 | * @param directory The new log file directory 177 | */ 178 | public void setDirectory(String directory) { 179 | 180 | String oldDirectory = this.directory; 181 | this.directory = directory; 182 | support.firePropertyChange("directory", oldDirectory, this.directory); 183 | 184 | } 185 | 186 | 187 | /** 188 | * Return the log file prefix. 189 | */ 190 | public String getPrefix() { 191 | 192 | return (prefix); 193 | 194 | } 195 | 196 | 197 | /** 198 | * Set the log file prefix. 199 | * 200 | * @param prefix The new log file prefix 201 | */ 202 | public void setPrefix(String prefix) { 203 | 204 | String oldPrefix = this.prefix; 205 | this.prefix = prefix; 206 | support.firePropertyChange("prefix", oldPrefix, this.prefix); 207 | 208 | } 209 | 210 | 211 | /** 212 | * Return the log file suffix. 213 | */ 214 | public String getSuffix() { 215 | 216 | return (suffix); 217 | 218 | } 219 | 220 | 221 | /** 222 | * Set the log file suffix. 223 | * 224 | * @param suffix The new log file suffix 225 | */ 226 | public void setSuffix(String suffix) { 227 | 228 | String oldSuffix = this.suffix; 229 | this.suffix = suffix; 230 | support.firePropertyChange("suffix", oldSuffix, this.suffix); 231 | 232 | } 233 | 234 | 235 | /** 236 | * Return the timestamp flag. 237 | */ 238 | public boolean getTimestamp() { 239 | 240 | return (timestamp); 241 | 242 | } 243 | 244 | 245 | /** 246 | * Set the timestamp flag. 247 | * 248 | * @param timestamp The new timestamp flag 249 | */ 250 | public void setTimestamp(boolean timestamp) { 251 | 252 | boolean oldTimestamp = this.timestamp; 253 | this.timestamp = timestamp; 254 | support.firePropertyChange("timestamp", new Boolean(oldTimestamp), 255 | new Boolean(this.timestamp)); 256 | 257 | } 258 | 259 | 260 | // --------------------------------------------------------- Public Methods 261 | 262 | 263 | /** 264 | * Writes the specified message to a servlet log file, usually an event 265 | * log. The name and type of the servlet log is specific to the 266 | * servlet container. 267 | * 268 | * @param msg A String specifying the message to be written 269 | * to the log file 270 | */ 271 | public void log(String msg) { 272 | 273 | // Construct the timestamp we will use, if requested 274 | Timestamp ts = new Timestamp(System.currentTimeMillis()); 275 | String tsString = ts.toString().substring(0, 19); 276 | String tsDate = tsString.substring(0, 10); 277 | 278 | // If the date has changed, switch log files 279 | if (!date.equals(tsDate)) { 280 | synchronized (this) { 281 | if (!date.equals(tsDate)) { 282 | close(); 283 | date = tsDate; 284 | open(); 285 | } 286 | } 287 | } 288 | 289 | // Log this message, timestamped if necessary 290 | if (writer != null) { 291 | if (timestamp) { 292 | writer.println(tsString + " " + msg); 293 | } else { 294 | writer.println(msg); 295 | } 296 | } 297 | 298 | } 299 | 300 | 301 | // -------------------------------------------------------- Private Methods 302 | 303 | 304 | /** 305 | * Close the currently open log file (if any) 306 | */ 307 | private void close() { 308 | 309 | if (writer == null) 310 | return; 311 | writer.flush(); 312 | writer.close(); 313 | writer = null; 314 | date = ""; 315 | 316 | } 317 | 318 | 319 | /** 320 | * Open the new log file for the date specified by date. 321 | */ 322 | private void open() { 323 | 324 | // Create the directory if necessary 325 | File dir = new File(directory); 326 | if (!dir.isAbsolute()) 327 | dir = new File(System.getProperty("catalina.base"), directory); 328 | dir.mkdirs(); 329 | 330 | // Open the current log file 331 | try { 332 | String pathname = dir.getAbsolutePath() + File.separator + 333 | prefix + date + suffix; 334 | writer = new PrintWriter(new FileWriter(pathname, true), true); 335 | } catch (IOException e) { 336 | writer = null; 337 | } 338 | 339 | } 340 | 341 | 342 | // ------------------------------------------------------ Lifecycle Methods 343 | 344 | 345 | /** 346 | * Add a lifecycle event listener to this component. 347 | * 348 | * @param listener The listener to add 349 | */ 350 | public void addLifecycleListener(LifecycleListener listener) { 351 | 352 | lifecycle.addLifecycleListener(listener); 353 | 354 | } 355 | 356 | 357 | /** 358 | * Get the lifecycle listeners associated with this lifecycle. If this 359 | * Lifecycle has no listeners registered, a zero-length array is returned. 360 | */ 361 | public LifecycleListener[] findLifecycleListeners() { 362 | 363 | return lifecycle.findLifecycleListeners(); 364 | 365 | } 366 | 367 | 368 | /** 369 | * Remove a lifecycle event listener from this component. 370 | * 371 | * @param listener The listener to add 372 | */ 373 | public void removeLifecycleListener(LifecycleListener listener) { 374 | 375 | lifecycle.removeLifecycleListener(listener); 376 | 377 | } 378 | 379 | 380 | /** 381 | * Prepare for the beginning of active use of the public methods of this 382 | * component. This method should be called after configure(), 383 | * and before any of the public methods of the component are utilized. 384 | * 385 | * @exception LifecycleException if this component detects a fatal error 386 | * that prevents this component from being used 387 | */ 388 | public void start() throws LifecycleException { 389 | 390 | // Validate and update our current component state 391 | if (started) 392 | throw new LifecycleException 393 | (sm.getString("fileLogger.alreadyStarted")); 394 | lifecycle.fireLifecycleEvent(START_EVENT, null); 395 | started = true; 396 | 397 | } 398 | 399 | 400 | /** 401 | * Gracefully terminate the active use of the public methods of this 402 | * component. This method should be the last one called on a given 403 | * instance of this component. 404 | * 405 | * @exception LifecycleException if this component detects a fatal error 406 | * that needs to be reported 407 | */ 408 | public void stop() throws LifecycleException { 409 | 410 | // Validate and update our current component state 411 | if (!started) 412 | throw new LifecycleException 413 | (sm.getString("fileLogger.notStarted")); 414 | lifecycle.fireLifecycleEvent(STOP_EVENT, null); 415 | started = false; 416 | 417 | close(); 418 | 419 | } 420 | 421 | 422 | } 423 | 424 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/LoggerBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/logger/LoggerBase.java,v 1.5 2002/01/25 20:12:20 amyroh Exp $ 3 | * $Revision: 1.5 $ 4 | * $Date: 2002/01/25 20:12:20 $ 5 | * 6 | * ==================================================================== 7 | * 8 | * The Apache Software License, Version 1.1 9 | * 10 | * Copyright (c) 1999 The Apache Software Foundation. All rights 11 | * reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 17 | * 1. Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * 20 | * 2. Redistributions in binary form must reproduce the above copyright 21 | * notice, this list of conditions and the following disclaimer in 22 | * the documentation and/or other materials provided with the 23 | * distribution. 24 | * 25 | * 3. The end-user documentation included with the redistribution, if 26 | * any, must include the following acknowlegement: 27 | * "This product includes software developed by the 28 | * Apache Software Foundation (http://www.apache.org/)." 29 | * Alternately, this acknowlegement may appear in the software itself, 30 | * if and wherever such third-party acknowlegements normally appear. 31 | * 32 | * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software 33 | * Foundation" must not be used to endorse or promote products derived 34 | * from this software without prior written permission. For written 35 | * permission, please contact apache@apache.org. 36 | * 37 | * 5. Products derived from this software may not be called "Apache" 38 | * nor may "Apache" appear in their names without prior written 39 | * permission of the Apache Group. 40 | * 41 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 | * SUCH DAMAGE. 53 | * ==================================================================== 54 | * 55 | * This software consists of voluntary contributions made by many 56 | * individuals on behalf of the Apache Software Foundation. For more 57 | * information on the Apache Software Foundation, please see 58 | * . 59 | * 60 | * [Additional notices, if required by prior licensing conditions] 61 | * 62 | */ 63 | 64 | 65 | package ImSoobin.Chap07; 66 | 67 | 68 | import java.beans.PropertyChangeSupport; 69 | import java.beans.PropertyChangeListener; 70 | import java.io.CharArrayWriter; 71 | import java.io.PrintWriter; 72 | import javax.servlet.ServletException; 73 | import org.apache.catalina.Container; 74 | import org.apache.catalina.LifecycleException; 75 | import org.apache.catalina.Logger; 76 | 77 | 78 | /** 79 | * Convenience base class for Logger implementations. The only 80 | * method that must be implemented is log(String msg), plus 81 | * any property setting and lifecycle methods required for configuration. 82 | * 83 | * @author Craig R. McClanahan 84 | * @version $Revision: 1.5 $ $Date: 2002/01/25 20:12:20 $ 85 | */ 86 | 87 | public abstract class LoggerBase 88 | implements Logger { 89 | 90 | 91 | // ----------------------------------------------------- Instance Variables 92 | 93 | 94 | /** 95 | * The Container with which this Logger has been associated. 96 | */ 97 | protected Container container = null; 98 | 99 | 100 | /** 101 | * The debugging detail level for this component. 102 | */ 103 | protected int debug = 0; 104 | 105 | 106 | /** 107 | * The descriptive information about this implementation. 108 | */ 109 | protected static final String info = 110 | "org.apache.catalina.logger.LoggerBase/1.0"; 111 | 112 | 113 | /** 114 | * The property change support for this component. 115 | */ 116 | protected PropertyChangeSupport support = new PropertyChangeSupport(this); 117 | 118 | 119 | /** 120 | * The verbosity level for above which log messages may be filtered. 121 | */ 122 | protected int verbosity = ERROR; 123 | 124 | 125 | // ------------------------------------------------------------- Properties 126 | 127 | 128 | /** 129 | * Return the Container with which this Logger has been associated. 130 | */ 131 | public Container getContainer() { 132 | 133 | return (container); 134 | 135 | } 136 | 137 | 138 | /** 139 | * Set the Container with which this Logger has been associated. 140 | * 141 | * @param container The associated Container 142 | */ 143 | public void setContainer(Container container) { 144 | 145 | Container oldContainer = this.container; 146 | this.container = container; 147 | support.firePropertyChange("container", oldContainer, this.container); 148 | 149 | } 150 | 151 | 152 | /** 153 | * Return the debugging detail level for this component. 154 | */ 155 | public int getDebug() { 156 | 157 | return (this.debug); 158 | 159 | } 160 | 161 | 162 | /** 163 | * Set the debugging detail level for this component. 164 | * 165 | * @param debug The new debugging detail level 166 | */ 167 | public void setDebug(int debug) { 168 | 169 | this.debug = debug; 170 | 171 | } 172 | 173 | 174 | /** 175 | * Return descriptive information about this Logger implementation and 176 | * the corresponding version number, in the format 177 | * <description>/<version>. 178 | */ 179 | public String getInfo() { 180 | 181 | return (info); 182 | 183 | } 184 | 185 | 186 | /** 187 | * Return the verbosity level of this logger. Messages logged with a 188 | * higher verbosity than this level will be silently ignored. 189 | */ 190 | public int getVerbosity() { 191 | 192 | return (this.verbosity); 193 | 194 | } 195 | 196 | 197 | /** 198 | * Set the verbosity level of this logger. Messages logged with a 199 | * higher verbosity than this level will be silently ignored. 200 | * 201 | * @param verbosity The new verbosity level 202 | */ 203 | public void setVerbosity(int verbosity) { 204 | 205 | this.verbosity = verbosity; 206 | 207 | } 208 | 209 | 210 | /** 211 | * Set the verbosity level of this logger. Messages logged with a 212 | * higher verbosity than this level will be silently ignored. 213 | * 214 | * @param verbosityLevel The new verbosity level, as a string 215 | */ 216 | public void setVerbosityLevel(String verbosity) { 217 | 218 | if ("FATAL".equalsIgnoreCase(verbosity)) 219 | this.verbosity = FATAL; 220 | else if ("ERROR".equalsIgnoreCase(verbosity)) 221 | this.verbosity = ERROR; 222 | else if ("WARNING".equalsIgnoreCase(verbosity)) 223 | this.verbosity = WARNING; 224 | else if ("INFORMATION".equalsIgnoreCase(verbosity)) 225 | this.verbosity = INFORMATION; 226 | else if ("DEBUG".equalsIgnoreCase(verbosity)) 227 | this.verbosity = DEBUG; 228 | 229 | } 230 | 231 | 232 | // --------------------------------------------------------- Public Methods 233 | 234 | 235 | /** 236 | * Add a property change listener to this component. 237 | * 238 | * @param listener The listener to add 239 | */ 240 | public void addPropertyChangeListener(PropertyChangeListener listener) { 241 | 242 | support.addPropertyChangeListener(listener); 243 | 244 | } 245 | 246 | 247 | /** 248 | * Writes the specified message to a servlet log file, usually an event 249 | * log. The name and type of the servlet log is specific to the 250 | * servlet container. This message will be logged unconditionally. 251 | * 252 | * @param message A String specifying the message to be 253 | * written to the log file 254 | */ 255 | public abstract void log(String msg); 256 | 257 | 258 | /** 259 | * Writes the specified exception, and message, to a servlet log file. 260 | * The implementation of this method should call 261 | * log(msg, exception) instead. This method is deprecated 262 | * in the ServletContext interface, but not deprecated here to avoid 263 | * many useless compiler warnings. This message will be logged 264 | * unconditionally. 265 | * 266 | * @param exception An Exception to be reported 267 | * @param msg The associated message string 268 | */ 269 | public void log(Exception exception, String msg) { 270 | 271 | log(msg, exception); 272 | 273 | } 274 | 275 | 276 | /** 277 | * Writes an explanatory message and a stack trace for a given 278 | * Throwable exception to the servlet log file. The name 279 | * and type of the servlet log file is specific to the servlet container, 280 | * usually an event log. This message will be logged unconditionally. 281 | * 282 | * @param msg A String that describes the error or 283 | * exception 284 | * @param throwable The Throwable error or exception 285 | */ 286 | public void log(String msg, Throwable throwable) { 287 | 288 | CharArrayWriter buf = new CharArrayWriter(); 289 | PrintWriter writer = new PrintWriter(buf); 290 | writer.println(msg); 291 | throwable.printStackTrace(writer); 292 | Throwable rootCause = null; 293 | if (throwable instanceof LifecycleException) 294 | rootCause = ((LifecycleException) throwable).getThrowable(); 295 | else if (throwable instanceof ServletException) 296 | rootCause = ((ServletException) throwable).getRootCause(); 297 | if (rootCause != null) { 298 | writer.println("----- Root Cause -----"); 299 | rootCause.printStackTrace(writer); 300 | } 301 | log(buf.toString()); 302 | 303 | } 304 | 305 | 306 | /** 307 | * Writes the specified message to the servlet log file, usually an event 308 | * log, if the logger is set to a verbosity level equal to or higher than 309 | * the specified value for this message. 310 | * 311 | * @param message A String specifying the message to be 312 | * written to the log file 313 | * @param verbosity Verbosity level of this message 314 | */ 315 | public void log(String message, int verbosity) { 316 | 317 | if (this.verbosity >= verbosity) 318 | log(message); 319 | 320 | } 321 | 322 | 323 | /** 324 | * Writes the specified message and exception to the servlet log file, 325 | * usually an event log, if the logger is set to a verbosity level equal 326 | * to or higher than the specified value for this message. 327 | * 328 | * @param message A String that describes the error or 329 | * exception 330 | * @param throwable The Throwable error or exception 331 | * @param verbosity Verbosity level of this message 332 | */ 333 | public void log(String message, Throwable throwable, int verbosity) { 334 | 335 | if (this.verbosity >= verbosity) 336 | log(message, throwable); 337 | 338 | } 339 | 340 | 341 | /** 342 | * Remove a property change listener from this component. 343 | * 344 | * @param listener The listener to remove 345 | */ 346 | public void removePropertyChangeListener(PropertyChangeListener listener) { 347 | 348 | support.removePropertyChangeListener(listener); 349 | 350 | } 351 | 352 | 353 | } 354 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 7장 - 로거 4 | 5 | ### 작성자 6 | 7 | 임수빈 8 | 9 | ## 로거 10 | 11 | 메시지를 기록해주는 컴포넌트 12 | 13 | 카탈리나에서의 로거는 컨테이너와 연결되어있으며, 다른 컴포넌트에 비해 상대적으로 간단하다 14 | org.apache.catalina.logger 패키지에 다양한 로거 제공함. 15 | 16 | __* 모든 로거는 org.apache.catalina.logger 인터페이스를 구현해야함.__ 17 | 18 | logger인터페이스는 이 인터페이스를 구현하는 클래스에서 선택할 수 있는 다양한 종류의 log메소드를 정의해놓고 있다. 19 | #### - Logger interface 20 | ```java 21 | import java.beans.PropertyChangeListener; 22 | 23 | public interface Logger{ 24 | public static final int FATAL = Integer.MIN_VALUE; 25 | public static final int ERROR = 1; 26 | public static final int WARNING = 2; 27 | public static final int INFORMATION =3; 28 | public static final int DEUG = 4; 29 | 30 | public Container getContainer(); 31 | public void setContainer(Container container); 32 | public String getInfo(); 33 | public int getVerbosity(); 34 | public void setVerbosity(int verbosity); 35 | public void addPropertyChangeListener(PropertyChangeListener listener); 36 | public void log(String message); 37 | public void log(Exception exception, String msg); 38 | public void log(String message, Throwable throwable); 39 | public void log(String message, int verbosity); 40 | public void log(String message, Throwable throwable, int verbosity); 41 | public void removePropertyChangeListener(PropertyChangeListener listener); 42 | } 43 | ``` 44 | 단 하나의 String을 전달받는 메소드인데, 문자열은 기록할 메시지 45 | 46 | log 메소드 2개 출력수준 (verbosity level)을 받는 메소드 47 | 클래스의 인스턴스에 지정된 출력 수준보다 낮은 수준의 값을 log메소드에 전달하면 로그메시지가 기록되며, 48 | 그렇지 않은 경우 메시지는 무시됨. 49 | 50 | ### 출력 수준 51 | FATAL (항상), ERROR (1), WARNING (2), INFORMATION (3), DEBUG (4) 52 | getVerbosity, setVerbosity메소드는 출력수준의 값을 얻거나 설정할 때 사용 53 | 54 | 추가로 Logger인터페이스는 연결돼있는 컨테이너와 관련한 getContainer와 setContainer메소드를 제공함 55 | 56 | ![Logger UML](./img/tomcatloger.jpg) 57 | ### Logger 종류 58 | SystemErrLogger, SystemOutLogger, FileLogger 59 | 톰캣은 3개의 Logger를 제공함. 60 | 61 | 모두 org.apache.catalina.logger 패키지에 있으며, org.apache.catalina.logger.LoggerBase 클래스를 확장하고 있다. 62 | ### LoggerBase 63 | LoggerBase클래스는 log(String msg)메소드를 제외한 Logger인터페이스의 모든 메소드를 구현해놓은 추상 클래스 64 | 65 | public abstract void log(String msg); 66 | 하위 클래스는 서로 다른 대상에 메시지를 기록할 수 있기 때문이다. 67 | 따라서, 이 메소드는 LoggerBase클래스에서 구현되어있지 않다 68 | 69 | >톰캣5에서의 LoggerBase클래스는 추가로 LifeCycle과 MBeanRegistration 인터페이스를 구현함. 70 | 71 | 72 | 73 | ```java 74 | //출력 수준을 설정하는 메소드 75 | public void setVerbosityLevel(String verbosity) 76 | { 77 | if ("FATAL".equalsIgnoreCase(verbosity)) 78 | this.verbosity = FATAL; 79 | else if ("ERROR".equalsIgnoreCase(verbosity)) 80 | this.verbosity = ERROR; 81 | else if ("WARNING".equalsIgnoreCase(verbosity)) 82 | this.verbosity = WARNING; 83 | else if ("INFORMATION".equalsIgnoreCase(verbosity)) 84 | this.verbosity = INFORMATION; 85 | else if ("DEBUG".equalsIgnoreCase(verbosity)) 86 | this.verbosity = DEBUG; 87 | } 88 | 89 | // 출력수준보다 낮은 로그 출력 90 | public void log(String message, int verbosity) 91 | { 92 | if (this.verbosity >= verbosity) 93 | log(message); 94 | } 95 | ``` 96 | 97 | 98 | 99 | 100 | ### SystemOutLogger 101 | 이 하위클래스는 log(String msg)메소드를 구현하고 있는데, 전달받은 모든 메시지는 102 | System.out.println 메소드로 다시 전달한다. 103 | ```java 104 | public class SystemOutLogger extends LoggerBase 105 | { 106 | protected static final String info = "org.apache.catalina.logger.SystemOutLogger/1.0"; 107 | 108 | public void log(String msg) 109 | { 110 | System.out.println(msg); 111 | } 112 | } 113 | ``` 114 | 115 | ### SystemErrLogger 116 | 이 클래스는 log(String)메시지가 System.err.println()으로 전달되는 것 외에 동일함. 117 | ```java 118 | public class SystemErrLogger extends LoggerBase 119 | { 120 | protected static final String info = "org.apache.catalina.logger.SystemOutLogger/1.0"; 121 | 122 | public void log(String msg) 123 | { 124 | System.err.println(msg); 125 | } 126 | } 127 | ``` 128 | 129 | ### FileLogger 130 | LoggerBase의 하위 클래스 중 가장 복잡한 클래스 131 | 연결되어있는 컨테이너로부터 메시지를 받아 파일에 기록함. 각 메시지 별로 타임스탬프를 같이 기록할 수도 있다. 132 | 처음 이 클래스의 인스턴스가 생성되면, 날짜 정보를 포함하는 이름을 갖는 파일을 생성한다. 133 | 날짜가 바뀌면 다시 새로운 날짜를 이름으로 갖는 파일을 생성함. 134 | 또, 이 클래스는 로그 파일의 이름에 접두어나 접미어를 붙일 수 있는 방법도 제공함. 135 | 136 | FileLogger클래스 내부 137 | ```java 138 | public class FileLogger extends LoggerBase implements Lifecycle 139 | { 140 | ... 141 | 142 | public void start() throws LifecycleException 143 | { 144 | // 현재 컴포넌트의 상태를 검증 및 갱신함 145 | if (started) 146 | throw new LifecycleException(sm.getString("fileLogger.alreadyStarted")); 147 | lifecycle.fireLifecycleEvent(START_EVENT, null); 148 | started = true; 149 | } 150 | public void stop() throws LifecycleException 151 | { 152 | // 현재 컴포넌트의 상태를 검증 및 갱신함 153 | if (!started) 154 | throw new LifecycleException(sm.getString("fileLogger.notStarted")); 155 | lifecycle.fireLifecycleEvent(STOP_EVENT, null); 156 | started = false; 157 | close(); 158 | } 159 | /* 받은 메시지를 로그파일에 기록함 FileLogger인스턴스의 생명주기동안, 160 | log메소드는 여러개의 파일을 열고 닫을 수 있다. 161 | 일반적으로, log메소드는 날짜가 바뀐 경우 현재의 파일을 닫고 새 파일을 여는 형식으로 162 | 로그파일을 전환한다. 163 | 164 | Timestamp클래스는 nanoseconds까지 나타냄 (19자) 165 | yyyy-mm-dd hh:mm:ss.fffffffff 166 | */ 167 | public void log(String msg) 168 | { 169 | // 타임스탬프 데이터의 생성 170 | Timestamp ts = new Timestamp(System.currentTimeMillis()); 171 | String tsString = ts.toString().substring(0, 19); 172 | String tsDate = tsString.substring(0, 10); 173 | 174 | // 필요하다면 타임스탬프를 붙여 메시지를 기록 175 | if (!date.equals(tsDate)) 176 | { 177 | synchronized (this) { 178 | if (!date.equals(tsDate)) { 179 | close(); 180 | date = tsDate; 181 | open(); 182 | } 183 | } 184 | } 185 | /* 로그 파일을 생성하려는 디렉토리가 이미 존재하는지 확인 186 | 존재하지 않으면 새디렉토리 생성 187 | PrintWrtier 인스턴스 생성 -> FileWriter객체를 사용하여 pathname에 해당하는 파일에 쓰기작업실행 188 | */ 189 | private void open() 190 | { 191 | // 필요하면 디렉토리를 생성시킴 192 | File dir = new File(directory); 193 | if (!dir.isAbsolute()) 194 | dir = new File(System.getProperty("catalina.base"), directory); 195 | dir.mkdirs(); 196 | 197 | // 현재 로그 파일을 연다 198 | try { 199 | String pathname = dir.getAbsolutePath() + File.separator + 200 | prefix + date + suffix; 201 | writer = new PrintWriter(new FileWriter(pathname, true), true); 202 | } catch (IOException e) { 203 | writer = null; 204 | } 205 | } 206 | /* writer버퍼의 내용을 스트림에 쓰게하고 printerWriter를 닫은 후 null을 할당함*/ 207 | private void close() 208 | { 209 | if (writer == null) 210 | return; 211 | writer.flush(); 212 | writer.close(); 213 | writer = null; 214 | date = ""; 215 | } 216 | ... 217 | } 218 | ``` 219 | ### 6장의 BootStrap에 로거 추가 220 | ```java 221 | import org.apache.catalina.Connector; 222 | import org.apache.catalina.Context; 223 | import org.apache.catalina.Lifecycle; 224 | import org.apache.catalina.LifecycleListener; 225 | import org.apache.catalina.Loader; 226 | import org.apache.catalina.logger.FileLogger; 227 | import org.apache.catalina.Mapper; 228 | import org.apache.catalina.Wrapper; 229 | import org.apache.catalina.connector.http.HttpConnector; 230 | 231 | public final class Bootstrap { 232 | public static void main(String[] args) { 233 | Connector connector = new HttpConnector(); 234 | Wrapper wrapper1 = new SimpleWrapper(); 235 | wrapper1.setName("Primitive"); 236 | wrapper1.setServletClass("PrimitiveServlet"); 237 | Wrapper wrapper2 = new SimpleWrapper(); 238 | wrapper2.setName("Modern"); 239 | wrapper2.setServletClass("ModernServlet"); 240 | Loader loader = new SimpleLoader(); 241 | 242 | Context context = new SimpleContext(); 243 | context.addChild(wrapper1); 244 | context.addChild(wrapper2); 245 | 246 | Mapper mapper = new SimpleContextMapper(); 247 | mapper.setProtocol("http"); 248 | LifecycleListener listener = new SimpleContextLifecycleListener(); 249 | ((Lifecycle) context).addLifecycleListener(listener); 250 | context.addMapper(mapper); 251 | context.setLoader(loader); 252 | // context.addServletMapping(pattern, name); 253 | context.addServletMapping("/Primitive", "Primitive"); 254 | context.addServletMapping("/Modern", "Modern"); 255 | 256 | // ********* 로거 추가 ********* 257 | System.setProperty("catalina.base", System.getProperty("user.dir")); 258 | FileLogger logger = new FileLogger(); 259 | logger.setPrefix("FileLog_"); 260 | logger.setSuffix(".txt"); 261 | logger.setTimestamp(true); 262 | logger.setDirectory("webroot"); 263 | context.setLogger(logger); 264 | 265 | //--------------------------- 266 | 267 | connector.setContainer(context); 268 | try { 269 | connector.initialize(); 270 | ((Lifecycle) connector).start(); 271 | ((Lifecycle) context).start(); 272 | 273 | // make the application wait until we press a key. 274 | System.in.read(); 275 | ((Lifecycle) context).stop(); 276 | } 277 | catch (Exception e) { 278 | e.printStackTrace(); 279 | } 280 | } 281 | } 282 | ``` 283 | 284 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/SystemErrLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/logger/SystemErrLogger.java,v 1.3 2002/04/26 21:09:06 craigmcc Exp $ 3 | * $Revision: 1.3 $ 4 | * $Date: 2002/04/26 21:09:06 $ 5 | * 6 | * ==================================================================== 7 | * 8 | * The Apache Software License, Version 1.1 9 | * 10 | * Copyright (c) 1999 The Apache Software Foundation. All rights 11 | * reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 17 | * 1. Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * 20 | * 2. Redistributions in binary form must reproduce the above copyright 21 | * notice, this list of conditions and the following disclaimer in 22 | * the documentation and/or other materials provided with the 23 | * distribution. 24 | * 25 | * 3. The end-user documentation included with the redistribution, if 26 | * any, must include the following acknowlegement: 27 | * "This product includes software developed by the 28 | * Apache Software Foundation (http://www.apache.org/)." 29 | * Alternately, this acknowlegement may appear in the software itself, 30 | * if and wherever such third-party acknowlegements normally appear. 31 | * 32 | * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software 33 | * Foundation" must not be used to endorse or promote products derived 34 | * from this software without prior written permission. For written 35 | * permission, please contact apache@apache.org. 36 | * 37 | * 5. Products derived from this software may not be called "Apache" 38 | * nor may "Apache" appear in their names without prior written 39 | * permission of the Apache Group. 40 | * 41 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 | * SUCH DAMAGE. 53 | * ==================================================================== 54 | * 55 | * This software consists of voluntary contributions made by many 56 | * individuals on behalf of the Apache Software Foundation. For more 57 | * information on the Apache Software Foundation, please see 58 | * . 59 | * 60 | * [Additional notices, if required by prior licensing conditions] 61 | * 62 | */ 63 | 64 | 65 | package ImSoobin.Chap07; 66 | 67 | 68 | /** 69 | * Simple implementation of Logger that writes to System.err. 70 | * 71 | * @author Craig R. McClanahan 72 | * @version $Revision: 1.3 $ $Date: 2002/04/26 21:09:06 $ 73 | */ 74 | 75 | public class SystemErrLogger 76 | extends LoggerBase { 77 | 78 | 79 | // ----------------------------------------------------- Instance Variables 80 | 81 | 82 | /** 83 | * The descriptive information about this implementation. 84 | */ 85 | protected static final String info = 86 | "org.apache.catalina.logger.SystemErrLogger/1.0"; 87 | 88 | 89 | // --------------------------------------------------------- Public Methods 90 | 91 | 92 | /** 93 | * Writes the specified message to a servlet log file, usually an event 94 | * log. The name and type of the servlet log is specific to the 95 | * servlet container. 96 | * 97 | * @param msg A String specifying the message to be written 98 | * to the log file 99 | */ 100 | public void log(String msg) { 101 | 102 | System.err.println(msg); 103 | 104 | } 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/SystemOutLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/logger/SystemOutLogger.java,v 1.3 2002/04/26 21:09:06 craigmcc Exp $ 3 | * $Revision: 1.3 $ 4 | * $Date: 2002/04/26 21:09:06 $ 5 | * 6 | * ==================================================================== 7 | * 8 | * The Apache Software License, Version 1.1 9 | * 10 | * Copyright (c) 1999 The Apache Software Foundation. All rights 11 | * reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 17 | * 1. Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * 20 | * 2. Redistributions in binary form must reproduce the above copyright 21 | * notice, this list of conditions and the following disclaimer in 22 | * the documentation and/or other materials provided with the 23 | * distribution. 24 | * 25 | * 3. The end-user documentation included with the redistribution, if 26 | * any, must include the following acknowlegement: 27 | * "This product includes software developed by the 28 | * Apache Software Foundation (http://www.apache.org/)." 29 | * Alternately, this acknowlegement may appear in the software itself, 30 | * if and wherever such third-party acknowlegements normally appear. 31 | * 32 | * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software 33 | * Foundation" must not be used to endorse or promote products derived 34 | * from this software without prior written permission. For written 35 | * permission, please contact apache@apache.org. 36 | * 37 | * 5. Products derived from this software may not be called "Apache" 38 | * nor may "Apache" appear in their names without prior written 39 | * permission of the Apache Group. 40 | * 41 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 | * SUCH DAMAGE. 53 | * ==================================================================== 54 | * 55 | * This software consists of voluntary contributions made by many 56 | * individuals on behalf of the Apache Software Foundation. For more 57 | * information on the Apache Software Foundation, please see 58 | * . 59 | * 60 | * [Additional notices, if required by prior licensing conditions] 61 | * 62 | */ 63 | 64 | 65 | package ImSoobin.Chap07; 66 | 67 | 68 | /** 69 | * Simple implementation of Logger that writes to System.out. 70 | * Because this component is so simple, no configuration is required. 71 | * Therefore, Lifecycle is not implemented. 72 | * 73 | * @author Craig R. McClanahan 74 | * @version $Revision: 1.3 $ $Date: 2002/04/26 21:09:06 $ 75 | */ 76 | 77 | public class SystemOutLogger 78 | extends LoggerBase { 79 | 80 | 81 | // ----------------------------------------------------- Instance Variables 82 | 83 | 84 | /** 85 | * The descriptive information about this implementation. 86 | */ 87 | protected static final String info = 88 | "org.apache.catalina.logger.SystemOutLogger/1.0"; 89 | 90 | 91 | // --------------------------------------------------------- Public Methods 92 | 93 | 94 | /** 95 | * Writes the specified message to a servlet log file, usually an event 96 | * log. The name and type of the servlet log is specific to the 97 | * servlet container. 98 | * 99 | * @param msg A String specifying the message to be written 100 | * to the log file 101 | */ 102 | public void log(String msg) { 103 | 104 | System.out.println(msg); 105 | 106 | } 107 | 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/img/tomcatloger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/ImSoobin/Chap07/img/tomcatloger.jpg -------------------------------------------------------------------------------- /src/ImSoobin/Chap07/startup/Bootstrap.java: -------------------------------------------------------------------------------- 1 | package ImSoobin.Chap07.startup; 2 | 3 | 4 | import org.apache.catalina.Connector; 5 | import org.apache.catalina.Context; 6 | import org.apache.catalina.Lifecycle; 7 | import org.apache.catalina.LifecycleListener; 8 | import org.apache.catalina.Loader; 9 | import org.apache.catalina.logger.FileLogger; 10 | import org.apache.catalina.Mapper; 11 | import org.apache.catalina.Wrapper; 12 | import org.apache.catalina.connector.http.HttpConnector; 13 | 14 | public final class Bootstrap { 15 | public static void main(String[] args) { 16 | Connector connector = new HttpConnector(); 17 | Wrapper wrapper1 = new SimpleWrapper(); 18 | wrapper1.setName("Primitive"); 19 | wrapper1.setServletClass("PrimitiveServlet"); 20 | Wrapper wrapper2 = new SimpleWrapper(); 21 | wrapper2.setName("Modern"); 22 | wrapper2.setServletClass("ModernServlet"); 23 | Loader loader = new SimpleLoader(); 24 | 25 | Context context = new SimpleContext(); 26 | context.addChild(wrapper1); 27 | context.addChild(wrapper2); 28 | 29 | Mapper mapper = new SimpleContextMapper(); 30 | mapper.setProtocol("http"); 31 | LifecycleListener listener = new SimpleContextLifecycleListener(); 32 | ((Lifecycle) context).addLifecycleListener(listener); 33 | context.addMapper(mapper); 34 | context.setLoader(loader); 35 | // context.addServletMapping(pattern, name); 36 | context.addServletMapping("/Primitive", "Primitive"); 37 | context.addServletMapping("/Modern", "Modern"); 38 | 39 | // ------ add logger -------- 40 | System.setProperty("catalina.base", System.getProperty("user.dir")); 41 | FileLogger logger = new FileLogger(); 42 | logger.setPrefix("FileLog_"); 43 | logger.setSuffix(".txt"); 44 | logger.setTimestamp(true); 45 | logger.setDirectory("webroot"); 46 | context.setLogger(logger); 47 | 48 | //--------------------------- 49 | 50 | connector.setContainer(context); 51 | try { 52 | connector.initialize(); 53 | ((Lifecycle) connector).start(); 54 | ((Lifecycle) context).start(); 55 | 56 | // make the application wait until we press a key. 57 | System.in.read(); 58 | ((Lifecycle) context).stop(); 59 | } 60 | catch (Exception e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/ImSoobin/Chap12/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 12장 - StandardContext 5 | 6 | ### 작성자 7 | 8 | 임수빈 9 | 10 | ## StandardContext 11 | 12 | 카탈리나에서의 Context 인터페이스의 표준 구현 클래스 13 | : org.apache.catalina.core.StandardContext 14 | 15 | StandardContext 인스턴스가 생성된 후, 16 | __들어오는 HTTP요청을 서비스할 수 있도록 start 메소드가 반드시 호출되야함__ 17 | 18 | ** StandardContext 객체는 Context가 %CATALINA_HOME%/conf 디렉토리에 있는 기본 web.xml 내용을 19 | 파싱해서, 배치돼있는 모든 애플리케이션에 적용할 수 있도록 준비됨. 20 | 21 | ### StandardContext 동작순서 22 | 1. StandardContext인스턴스 생성(초기화) 23 | 24 | 2. start 메소드 호출 25 | 26 | 3. 생명주기 리스너(이벤트) 발생 27 | 28 | 4. 이벤트에서 리스너 호출 -> StandardContext 인스턴스 설정 29 | 30 | 5. 설정이 올바르게 완료됨. - configured = true 31 | 설정이 올바르게 완료안됨. - configured =false 32 | configured = false -> HTTP 요청을 서비스하지 못함 - stop메소드를 호출 33 | >** configured : 올바르게 설정됬는지 여부를 나타내는 boolean타입의 변수 34 | 35 | 6. 인스턴스 올바르게 설정되고 관련 하위 컨테이너와 컴포넌트들이 성공적으로 구동시 36 | -> available = true StandardContext 37 | available = false는 반대의 의미 38 | 39 | #### 1. StandardContext인스턴스 생성(초기화) 40 | -- StandardContext 생성자 41 | ```java 42 | public StandardContext() 43 | { 44 | super(); 45 | pipeline.setBasic(new StandardContextValve()); 46 | namingResources.setContainer(this); 47 | } 48 | ``` 49 | org.apache.catalina.core.StandardContextValve 타입의 기본 밸브가 주어짐 50 | 커넥터를 통해 들어오는 모든 HTTP 요청들을 처리할 것임 51 | 52 | #### 2. start 메소드 호출 53 | StandardContext의 start메소드가 호출되면, 생명주기 이벤트가 발생함. 54 | > ** standardContext는 자신의 설정자(configurator)로서 이벤트 리스너를 사용함. 55 | 56 | start메소드가 성공적으로 수행되기 위해 StandardContext객체가 올바르게 설정되야함. 57 | 58 | >** StandardContext를 설정하는 생명주기 리스너는 org.apache.catalina.startup.ContexstConfig타입의 리스너(15장) 59 | 60 | #### **- StandardContext 클래스의 start메소드** 61 | 1 - BEFORE_START_EVENT 이벤트 발생 62 | 63 | 2 -1 available 특성이 값을 false로 지정 64 | 65 | -2 configured 특성의 값을 false로 지정 66 | 67 | 3 - 지원 준비 68 | 69 | -1 로더 준비 70 | 71 | -2 매니저 준비 72 | 73 | 4 - 문자셋 맵퍼 초기화 74 | 75 | 5 - 이 컨텍스트와 연관된 다른 컴포넌트 구동 76 | 77 | 6 - 하위 컨테이너(래퍼) 구동 78 | 79 | 7 - 파이프라인 구동 80 | 81 | 8 - 매니저 구동 82 | 83 | 9 - START_EVENT 이벤트 발생 - 리스너가 설정작업 수행 (완료시 configured = true) 84 | 85 | 10 -1 configured값 확인 true라면, postWelcomePages 메소드 호출, 86 | 87 | 구동 시에 로드할 하위 래퍼들을 로드, available특성을 true로 지정 88 | 89 | -1 configured값이 false라면, stop메소드 호출 90 | 91 | 11 - AFTER_START_EVENT 이벤트 발생 92 | 93 | ### StandardContextMapper 94 | 들어오는 요청에 대해서는 StandardContext 파이프라인에 있는 기본 밸브의 invoke 메소드가 호출됨. 95 | invoke메소드에서 가장 먼저 필요한 것은 요청을 처리할 래퍼를 얻는 것임 96 | 적절한 래퍼를 찾기 위해 사용하는 것이 StandardContextMapper 97 | 98 | ### 재로딩 지원 99 | reloadable 특성을 사용함. (default : false) 100 | 재로드하는데 있어서 자신의 로더에 의존함. 101 | 모든 클래스들과 JAR파일들으 타임스탬프를 확인하는 스레드를 갖고 있어 102 | 로더와 StandardContext에 연결하여 지속적으로 확인하는 스레드를 이용하여 재로딩 수행 103 | ** 서버에 부담이 갈 수있으므로 지양 104 | 105 | ### backgroundProcess 메소드 106 | Context는 로더나 메니저와 같은 다른 컴포넌트들의 도움이 필요함. 107 | 이러한 컴포넌트들은 백그라운드에서 일을 처리하기 위해 별도의 스레드를 실행해야함. 108 | > EX) 매니저 컴포넌트의 경우, 자신이 관리하는 세션 객체의 만료 시간을 확인하기 위한 스레드 필요 109 | 110 | ** 톰캣 4 - 컴포넌트가 자신의 스레드를 사용해 일을 처리함 111 | ** 톰캣 5 - 자원을 절약하기 위해 모든 백그라운드 프로세스는 동일한 스레드를 공유 시킴 112 | -------------------------------------------------------------------------------- /src/NamGiUng/Chap03/README.md: -------------------------------------------------------------------------------- 1 | # 3장 커넥터 2 | 3 | ### 작성자 4 | 5 | **남기웅** 6 | 7 | ## 학습 목표 8 | 9 | - 커넥터의 역할을 이해한다. 10 | 11 | - 2장의 애플리케이션보다 개선된 사항을 이해한다. 12 | 13 | 14 | 15 | ## 전체적인 그림 16 | 17 | ![](./img/UML03.jpg) 18 | 19 | 20 | 21 | 애플리케이션은 크게 3개의 모듈로 나뉘어져 있다. 22 | 23 | | 구성모듈 | 역할 | 24 | | :-------: | :----------------------------------------------------------: | 25 | | startup | main theread 실행 | 26 | | connector | 클라이언트 요청대기 및 통신소켓 생성,
요청메시지 파악 후 처리객체 생성 | 27 | | core | 해당 요청 처리 객체 | 28 | 29 | 애플리케이션의 개략적인 흐름은 다음과같다. 30 | 31 | 1. Bootstrap 클래스에서 main 스레드를 실행하고 main스레드가 실행되면 HttpConnector 객체를 생성 후 start()를 실행 32 | 2. start() 메소드 에서는 서버소켓 생성 후 accept() , 요청이 들어오면 새로운 통신소켓을 생성하여 클라이언트와 통신할 준비를 하고 HttpProcessor 객체를 생성후 소켓객체를 넘겨주면서 process()를실행한다. 33 | 3. process()HttpRequest(요청)객체와 HttpResponse(응답)객체를 생성하고 요청메시지를 파싱하여 정적자원에 대한 요청인지 동적자원에 대한 요청인지 판단 후, 정적요청이면 StaticResourceProcessor 객체동적요청이면 ServletProcessor 객체를 생성하여 실행한다. 34 | 4. , 요청결과를 클라이언트에게 전송해주고 통신은 종료된다. 35 | 36 | 37 | 38 | ## 커넥터 39 | 40 | 목적 : 자원요청을 기다리는 부분을 하나의 모듈로 만듬으로써 유지보수 및 기능확장을 쉽게하기위함. 41 | 42 | 기능 43 | 44 | - 클라이언트로부터의 요청을 기다리고 요청이들어오면 새로운 통신소켓을 만들어 통신한다. 45 | - 요청객체와 응답객체를 생성한다. 46 | 47 | 48 | 49 | ## StringManager 클래스 50 | 51 | 목적 : 대형 애플리케이션에서 에러 발생시 , 에러에 대한 인지 및 해결을 손쉽게 하기위한 class 52 | 53 | 방법 54 | 55 | - 각 패키지에 properties 파일에 각 에러사항에 대한 메시지를 mapping 시켜 적어놓는다. 56 | 57 | - 각 패키지의 클래스는 여러개이므로 메모리공간을 효율적으로 관리하기 위해 싱글톤 패턴을 적용 58 | 59 | - 에러가 발생시 StringManager는 패키지 내에 properties파일을 읽어들여 발생 에러와 mapping 60 | 61 | 되 있는 에러메시지를 출력한다. 62 | 63 | 64 | 65 | ## 애플리케이션 66 | 67 | 실행 흐름 68 | 69 | ![](./img/Flow.png) 70 | 71 | 72 | 73 | 74 | 75 | ## 2장과의 비교 76 | 77 | - 커넥터 class를 정의하여 클라이언트로부터의 요청을 수락 및 소켓생성 부분을 모듈화하였다. 78 | - 요청객체는 요청 헤더를 파싱할 수 있으며 , 파라미터 값을 얻을 수 있도록 발전하였다. 79 | - 응답객체는 PrintWriter의 print()또한 autoflush기능을 갖도록 성능을 개선하였다. 80 | 81 | -------------------------------------------------------------------------------- /src/NamGiUng/Chap03/img/Flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/NamGiUng/Chap03/img/Flow.png -------------------------------------------------------------------------------- /src/NamGiUng/Chap03/img/UML03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/NamGiUng/Chap03/img/UML03.jpg -------------------------------------------------------------------------------- /src/NamGiUng/Chap08/README.md: -------------------------------------------------------------------------------- 1 | # 8. 로더 2 | 3 | ### 작성자 4 | 5 | 남기웅 6 | 7 | ## 학습 목표 8 | 9 | - 톰캣에서의 클래스로더를 이해한다. 10 | 11 | 12 | 13 | ## JVM(자바 가상 머신) 14 | 15 | ![](./img/JVM.png) 16 | 17 | 클래스로더는 JVM의 method area에 .class file을 적재시켜주는 역할을 한다. 18 | 19 | 톰캣에서는 하나의 JVM객체를 여러 컨텍스트가 공유해서 쓰며 각각의 컨텍스트는 자기자신만의 20 | 21 | 로더를 가진다. 22 | 23 | 24 | 25 | ## 클래스로더 26 | 27 | - 자바 클래스로더 28 | 29 | ![](./img/img01.png) 30 | 31 | 32 | 33 | 34 | 35 | | 클래스로더 | 설명 | 36 | | :--------: | :-------------------------------------------: | 37 | | BootStrap | JVM이 실행되기 위해 필요한 .class 파일을 로드 | 38 | | Extension | Java의 기본 package를 load | 39 | | System | 사용자가 작성한 .class 파일을 로드 | 40 | 41 | 일반적인 클래스로더는 위임(delegation)모델을 기준으로 하여 동작한다. 42 | 43 | - 위임모델 : 어떤 객체를 실행해야할 경우 , 그 객체의 클래스 파일이 메모리에 적재되어 있지 않을경우 하위 클래스 로더인 system 이 바로 탐색하지 않고 상위 클래스 로더에게 위임하는 모델. 44 | 45 | 46 | 47 | 48 | 49 | ## Tomcat 클래스 로더 50 | 51 | Tomcat 클래스 로더는 일반 JAVA 클래스 로더와는 기능이 다르다. 그 이유로는 다음과 같은 두가지 52 | 53 | 이유가 있다. 54 | 55 | 1. 보안 문제 : Tomcat 클래스 로더는 접근 디렉토리를 WEB-INF/classes , WEB-INF/lib 로 제한되어야 한다. CLASSPATH를 접근할 수 없어야 한다. 또한 하나의 컨텍스트 컨테이너는 자기자신만의 56 | 57 | 로더를 가지고 있어야 한다. 58 | 59 | 2. 재로딩(Leloading) : 서블릿 파일이 update되면 톰캣을 끄지 않고 재적재할 수 있어야 한다. 60 | 61 | 62 | 63 | => Tomcat의 클래스 로더는 보안상의 목적을 위하여 기존의 위임모델이 아닌 하위 클래스 로더부터 64 | 65 | 클래스의 탐색을 시작한다. 66 | 67 | 68 | 69 | 70 | 71 | ## 클래스 다이어그램 72 | 73 | ![](./img/diagram.jpg) 74 | 75 | 76 | 77 | | 클래스 (or 인터페이스) | 설명 | 78 | | ---------------------- | ----------------------------------------------------------- | 79 | | Loader | 클래스 로더의 기본 인터페이스 | 80 | | Reloader | 재적재 기능을 정의한 인터페이스 | 81 | | URLClassLoader | WebappClassLoader의 상위개념 | 82 | | WebappLoader | 실제 클래스 로더를 실행시키며 접근제어 , 리로딩을 하는 객체 | 83 | | WebappClassLoader | 컨텍스트가 갖는 개별적인 클래스 로더 | 84 | 85 | 86 | 87 | ## 참고 문헌 88 | 89 | 톰캣 최종분석 -------------------------------------------------------------------------------- /src/NamGiUng/Chap08/img/JVM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/NamGiUng/Chap08/img/JVM.png -------------------------------------------------------------------------------- /src/NamGiUng/Chap08/img/diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/NamGiUng/Chap08/img/diagram.jpg -------------------------------------------------------------------------------- /src/NamGiUng/Chap08/img/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/NamGiUng/Chap08/img/img01.png -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/Constants.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.File; 4 | 5 | public class Constants { 6 | static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; 7 | } 8 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/HttpServer1.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.InetAddress; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | public class HttpServer1 { 11 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // 종료 명령 12 | private boolean shutdown = false; // 종료 명령을 받았는가? 13 | 14 | public static void main(String[] args) { 15 | HttpServer1 server = new HttpServer1(); 16 | server.await(); 17 | } 18 | 19 | private void await() { 20 | ServerSocket serverSocket = null; 21 | int port = 8080; 22 | String host = "127.0.0.1"; 23 | 24 | try { 25 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName(host)); 26 | } catch(IOException e) { 27 | e.printStackTrace(); 28 | System.exit(1); 29 | } 30 | 31 | // 요청을 기다리는 루프 32 | while(!shutdown) { 33 | Socket socket = null; 34 | InputStream input = null; 35 | OutputStream output = null; 36 | try { 37 | socket = serverSocket.accept(); 38 | input = socket.getInputStream(); 39 | output = socket.getOutputStream(); 40 | 41 | // request 객체 생성 및 parse 호출 42 | Request request = new Request(input); 43 | request.parse(); 44 | 45 | // response 객체 생성 46 | Response response = new Response(output); 47 | response.setRequest(request); 48 | 49 | // 서블릿에 대한 요청("/servlet/")인지 정적 자원에 대한 요청인지 확인 50 | if(request.getUri().startsWith("/servlet/")) { // 서블릿 51 | ServletProcessor1 processor = new ServletProcessor1(); 52 | processor.process(request, response); 53 | } else { // 정적 자원 54 | StaticResourceProcessor processor = new StaticResourceProcessor(); 55 | processor.process(request, response); 56 | } 57 | 58 | // 소켓 닫기 59 | socket.close(); 60 | // URL이 종료 명령이었는지 확인 61 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 62 | } catch(Exception e) { 63 | e.printStackTrace(); 64 | System.exit(1); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/HttpServer2.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.InetAddress; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | public class HttpServer2 { 11 | private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // 종료 명령 12 | private boolean shutdown = false; // 종료 명령을 받았는가? 13 | 14 | public static void main(String[] args) { 15 | HttpServer2 server = new HttpServer2(); 16 | server.await(); 17 | } 18 | 19 | private void await() { 20 | ServerSocket serverSocket = null; 21 | int port = 8080; 22 | String host = "127.0.0.1"; 23 | 24 | try { 25 | serverSocket = new ServerSocket(port, 1, InetAddress.getByName(host)); 26 | } catch(IOException e) { 27 | e.printStackTrace(); 28 | System.exit(1); 29 | } 30 | 31 | // 요청을 기다리는 루프 32 | while(!shutdown) { 33 | Socket socket = null; 34 | InputStream input = null; 35 | OutputStream output = null; 36 | try { 37 | socket = serverSocket.accept(); 38 | input = socket.getInputStream(); 39 | output = socket.getOutputStream(); 40 | 41 | // request 객체 생성 및 parse 호출 42 | Request request = new Request(input); 43 | request.parse(); 44 | 45 | // response 객체 생성 46 | Response response = new Response(output); 47 | response.setRequest(request); 48 | 49 | // 서블릿에 대한 요청("/servlet/")인지 정적 자원에 대한 요청인지 확인 50 | if(request.getUri().startsWith("/servlet/")) { // 서블릿 51 | ServletProcessor2 processor = new ServletProcessor2(); 52 | processor.process(request, response); 53 | } else { // 정적 자원 54 | StaticResourceProcessor processor = new StaticResourceProcessor(); 55 | processor.process(request, response); 56 | } 57 | 58 | // 소켓 닫기 59 | socket.close(); 60 | // URL이 종료 명령이었는지 확인 61 | shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 62 | } catch(Exception e) { 63 | e.printStackTrace(); 64 | System.exit(1); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/Request.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.UnsupportedEncodingException; 7 | import java.util.Enumeration; 8 | import java.util.Locale; 9 | import java.util.Map; 10 | 11 | import javax.servlet.AsyncContext; 12 | import javax.servlet.DispatcherType; 13 | import javax.servlet.RequestDispatcher; 14 | import javax.servlet.ServletContext; 15 | import javax.servlet.ServletInputStream; 16 | import javax.servlet.ServletRequest; 17 | import javax.servlet.ServletResponse; 18 | 19 | public class Request implements ServletRequest { 20 | 21 | private InputStream input; 22 | private String uri; 23 | 24 | public Request(InputStream input) { 25 | this.input = input; 26 | } 27 | 28 | public String getUri() { 29 | return uri; 30 | } 31 | 32 | private String parseUri(String requestString) { 33 | int index1, index2; 34 | index1 = requestString.indexOf(' '); 35 | if(index1 != -1) { 36 | index2 = requestString.indexOf(' ', index1 + 1); 37 | if(index2 > index1) { 38 | return requestString.substring(index1 + 1, index2); 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | // 소켓으로부터 일련의 문자들을 읽음 45 | public void parse() { 46 | StringBuffer request = new StringBuffer(2048); 47 | int i; 48 | byte[] buffer = new byte[2048]; 49 | try { 50 | i = input.read(buffer); 51 | } catch(IOException e) { 52 | e.printStackTrace(); 53 | i = -1; 54 | } 55 | for(int j = 0; j < i; j++) { 56 | request.append((char) buffer[j]); 57 | } 58 | System.out.print(request.toString()); 59 | uri = parseUri(request.toString()); 60 | } 61 | 62 | /* 이하 ServletRequest의 구현 */ 63 | @Override 64 | public AsyncContext getAsyncContext() { 65 | return null; 66 | } 67 | @Override 68 | public Object getAttribute(String arg0) { 69 | return null; 70 | } 71 | @Override 72 | public Enumeration getAttributeNames() { 73 | return null; 74 | } 75 | @Override 76 | public String getCharacterEncoding() { 77 | return null; 78 | } 79 | @Override 80 | public int getContentLength() { 81 | return 0; 82 | } 83 | @Override 84 | public String getContentType() { 85 | return null; 86 | } 87 | @Override 88 | public DispatcherType getDispatcherType() { 89 | return null; 90 | } 91 | @Override 92 | public ServletInputStream getInputStream() throws IOException { 93 | return null; 94 | } 95 | @Override 96 | public String getLocalAddr() { 97 | return null; 98 | } 99 | @Override 100 | public String getLocalName() { 101 | return null; 102 | } 103 | @Override 104 | public int getLocalPort() { 105 | return 0; 106 | } 107 | @Override 108 | public Locale getLocale() { 109 | return null; 110 | } 111 | @Override 112 | public Enumeration getLocales() { 113 | return null; 114 | } 115 | @Override 116 | public String getParameter(String arg0) { 117 | return null; 118 | } 119 | @Override 120 | public Map getParameterMap() { 121 | return null; 122 | } 123 | @Override 124 | public Enumeration getParameterNames() { 125 | return null; 126 | } 127 | @Override 128 | public String[] getParameterValues(String arg0) { 129 | return null; 130 | } 131 | @Override 132 | public String getProtocol() { 133 | return null; 134 | } 135 | @Override 136 | public BufferedReader getReader() throws IOException { 137 | return null; 138 | } 139 | @Override 140 | public String getRealPath(String arg0) { 141 | return null; 142 | } 143 | @Override 144 | public String getRemoteAddr() { 145 | return null; 146 | } 147 | @Override 148 | public String getRemoteHost() { 149 | return null; 150 | } 151 | @Override 152 | public int getRemotePort() { 153 | return 0; 154 | } 155 | @Override 156 | public RequestDispatcher getRequestDispatcher(String arg0) { 157 | return null; 158 | } 159 | @Override 160 | public String getScheme() { 161 | return null; 162 | } 163 | @Override 164 | public String getServerName() { 165 | return null; 166 | } 167 | @Override 168 | public int getServerPort() { 169 | return 0; 170 | } 171 | @Override 172 | public ServletContext getServletContext() { 173 | return null; 174 | } 175 | @Override 176 | public boolean isAsyncStarted() { 177 | return false; 178 | } 179 | @Override 180 | public boolean isAsyncSupported() { 181 | return false; 182 | } 183 | @Override 184 | public boolean isSecure() { 185 | return false; 186 | } 187 | @Override 188 | public void removeAttribute(String arg0) { 189 | } 190 | @Override 191 | public void setAttribute(String arg0, Object arg1) { 192 | } 193 | @Override 194 | public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException { 195 | } 196 | @Override 197 | public AsyncContext startAsync() throws IllegalStateException { 198 | return null; 199 | } 200 | @Override 201 | public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) throws IllegalStateException { 202 | return null; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/RequestFacade.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.UnsupportedEncodingException; 7 | import java.util.Enumeration; 8 | import java.util.Locale; 9 | import java.util.Map; 10 | 11 | import javax.servlet.AsyncContext; 12 | import javax.servlet.DispatcherType; 13 | import javax.servlet.RequestDispatcher; 14 | import javax.servlet.ServletContext; 15 | import javax.servlet.ServletInputStream; 16 | import javax.servlet.ServletRequest; 17 | import javax.servlet.ServletResponse; 18 | 19 | public class RequestFacade implements ServletRequest { 20 | private ServletRequest request = null; 21 | 22 | public RequestFacade(Request request) { 23 | this.request = request; 24 | } 25 | 26 | /* 이하 ServletRequest의 구현 */ 27 | // facade 클래스를 이용하여 보안 문제 해결 28 | @Override 29 | public AsyncContext getAsyncContext() { 30 | return request.getAsyncContext(); 31 | } 32 | @Override 33 | public Object getAttribute(String arg0) { 34 | return request.getAttribute(arg0); 35 | } 36 | @Override 37 | public Enumeration getAttributeNames() { 38 | return request.getAttributeNames(); 39 | } 40 | @Override 41 | public String getCharacterEncoding() { 42 | return null; 43 | } 44 | @Override 45 | public int getContentLength() { 46 | return 0; 47 | } 48 | @Override 49 | public String getContentType() { 50 | return null; 51 | } 52 | @Override 53 | public DispatcherType getDispatcherType() { 54 | return null; 55 | } 56 | @Override 57 | public ServletInputStream getInputStream() throws IOException { 58 | return null; 59 | } 60 | @Override 61 | public String getLocalAddr() { 62 | return null; 63 | } 64 | @Override 65 | public String getLocalName() { 66 | return null; 67 | } 68 | @Override 69 | public int getLocalPort() { 70 | return 0; 71 | } 72 | @Override 73 | public Locale getLocale() { 74 | return null; 75 | } 76 | @Override 77 | public Enumeration getLocales() { 78 | return null; 79 | } 80 | @Override 81 | public String getParameter(String arg0) { 82 | return null; 83 | } 84 | @Override 85 | public Map getParameterMap() { 86 | return null; 87 | } 88 | @Override 89 | public Enumeration getParameterNames() { 90 | return null; 91 | } 92 | @Override 93 | public String[] getParameterValues(String arg0) { 94 | return null; 95 | } 96 | @Override 97 | public String getProtocol() { 98 | return null; 99 | } 100 | @Override 101 | public BufferedReader getReader() throws IOException { 102 | return null; 103 | } 104 | @Override 105 | public String getRealPath(String arg0) { 106 | return null; 107 | } 108 | @Override 109 | public String getRemoteAddr() { 110 | return null; 111 | } 112 | @Override 113 | public String getRemoteHost() { 114 | return null; 115 | } 116 | @Override 117 | public int getRemotePort() { 118 | return 0; 119 | } 120 | @Override 121 | public RequestDispatcher getRequestDispatcher(String arg0) { 122 | return null; 123 | } 124 | @Override 125 | public String getScheme() { 126 | return null; 127 | } 128 | @Override 129 | public String getServerName() { 130 | return null; 131 | } 132 | @Override 133 | public int getServerPort() { 134 | return 0; 135 | } 136 | @Override 137 | public ServletContext getServletContext() { 138 | return null; 139 | } 140 | @Override 141 | public boolean isAsyncStarted() { 142 | return false; 143 | } 144 | @Override 145 | public boolean isAsyncSupported() { 146 | return false; 147 | } 148 | @Override 149 | public boolean isSecure() { 150 | return false; 151 | } 152 | @Override 153 | public void removeAttribute(String arg0) { 154 | } 155 | @Override 156 | public void setAttribute(String arg0, Object arg1) { 157 | } 158 | @Override 159 | public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException { 160 | } 161 | @Override 162 | public AsyncContext startAsync() throws IllegalStateException { 163 | return null; 164 | } 165 | @Override 166 | public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) throws IllegalStateException { 167 | return null; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/Response.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.io.PrintWriter; 9 | import java.util.Locale; 10 | 11 | import javax.servlet.ServletOutputStream; 12 | import javax.servlet.ServletResponse; 13 | 14 | public class Response implements ServletResponse { 15 | 16 | private static final int BUFFER_SIZE = 1024; 17 | Request request; 18 | OutputStream output; 19 | PrintWriter writer; 20 | 21 | public Response(OutputStream output) { 22 | this.output = output; 23 | } 24 | 25 | public void setRequest(Request request) { 26 | this.request = request; 27 | } 28 | 29 | // 정적 페이지 서비스할 때 사용하는 메소드 30 | public void sendStaticResource() throws IOException { 31 | byte[] bytes = new byte[BUFFER_SIZE]; 32 | FileInputStream fis = null; 33 | try { 34 | File file = new File(Constants.WEB_ROOT, request.getUri()); 35 | /* 36 | * HTTP Response = Status-Line 37 | * (( general-header | response-header | entity-header ) CRLF ) 38 | * CRLF 39 | * [ message-body ] 40 | * Status-line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 41 | */ 42 | int ch = fis.read(bytes, 0, BUFFER_SIZE); 43 | while(ch != -1) { 44 | output.write(bytes, 0, ch); 45 | ch = fis.read(bytes, 0, BUFFER_SIZE); 46 | } 47 | } catch(FileNotFoundException e) { 48 | String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + 49 | "Content-Type: text/html\r\n" + 50 | "Content-Length: 23\r\n" + 51 | "\r\n" + 52 | "

File Not Found

"; 53 | output.write(errorMessage.getBytes()); 54 | } finally { 55 | if(fis != null) { 56 | fis.close(); 57 | } 58 | } 59 | } 60 | 61 | /* 이하 ServletResponse의 구현 */ 62 | @Override 63 | public void flushBuffer() throws IOException { 64 | } 65 | @Override 66 | public int getBufferSize() { 67 | return 0; 68 | } 69 | @Override 70 | public String getCharacterEncoding() { 71 | return null; 72 | } 73 | @Override 74 | public String getContentType() { 75 | return null; 76 | } 77 | @Override 78 | public Locale getLocale() { 79 | return null; 80 | } 81 | @Override 82 | public ServletOutputStream getOutputStream() throws IOException { 83 | return null; 84 | } 85 | 86 | @Override 87 | public PrintWriter getWriter() throws IOException { 88 | // autoflush가 true일 경우, println()은 출력 버퍼의 내용을 스트림에 전송하지만, print()는 그렇지 않다. 89 | writer = new PrintWriter(output, true); 90 | return writer; 91 | } 92 | 93 | @Override 94 | public boolean isCommitted() { 95 | return false; 96 | } 97 | @Override 98 | public void reset() { 99 | } 100 | @Override 101 | public void resetBuffer() { 102 | } 103 | @Override 104 | public void setBufferSize(int arg0) { 105 | } 106 | @Override 107 | public void setCharacterEncoding(String arg0) { 108 | } 109 | @Override 110 | public void setContentLength(int arg0) { 111 | } 112 | @Override 113 | public void setContentType(String arg0) { 114 | } 115 | @Override 116 | public void setLocale(Locale arg0) { 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/ResponseFacade.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.io.PrintWriter; 9 | import java.util.Locale; 10 | 11 | import javax.servlet.ServletOutputStream; 12 | import javax.servlet.ServletRequest; 13 | import javax.servlet.ServletResponse; 14 | 15 | public class ResponseFacade implements ServletResponse { 16 | private ServletResponse response = null; 17 | 18 | public ResponseFacade(Response response) { 19 | this.response = response; 20 | } 21 | 22 | /* 이하 ServletResponse의 구현 */ 23 | // facade 클래스를 이용하여 보안 문제 해결 24 | @Override 25 | public void flushBuffer() throws IOException { 26 | } 27 | @Override 28 | public int getBufferSize() { 29 | return response.getBufferSize(); 30 | } 31 | @Override 32 | public String getCharacterEncoding() { 33 | return response.getCharacterEncoding(); 34 | } 35 | @Override 36 | public String getContentType() { 37 | return response.getContentType(); 38 | } 39 | @Override 40 | public Locale getLocale() { 41 | return null; 42 | } 43 | @Override 44 | public ServletOutputStream getOutputStream() throws IOException { 45 | return null; 46 | } 47 | 48 | @Override 49 | public PrintWriter getWriter() throws IOException { 50 | return response.getWriter(); 51 | } 52 | 53 | @Override 54 | public boolean isCommitted() { 55 | return false; 56 | } 57 | @Override 58 | public void reset() { 59 | } 60 | @Override 61 | public void resetBuffer() { 62 | } 63 | @Override 64 | public void setBufferSize(int arg0) { 65 | } 66 | @Override 67 | public void setCharacterEncoding(String arg0) { 68 | } 69 | @Override 70 | public void setContentLength(int arg0) { 71 | } 72 | @Override 73 | public void setContentType(String arg0) { 74 | } 75 | @Override 76 | public void setLocale(Locale arg0) { 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/ServletProcessor1.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.net.URLClassLoader; 7 | import java.net.URLStreamHandler; 8 | 9 | import javax.servlet.Servlet; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | 13 | // 서블릿의 HTTP 요청을 처리하는 역할을 한다. 14 | public class ServletProcessor1 { 15 | 16 | public void process(Request request, Response response) { 17 | String uri = request.getUri(); 18 | String servletName = uri.substring(uri.lastIndexOf("/") + 1); 19 | URLClassLoader loader = null; 20 | try { 21 | // URLClassLoader의 생성 22 | URL[] urls = new URL[1]; 23 | URLStreamHandler streamHandler = null; 24 | File classPath = new File(Constants.WEB_ROOT); 25 | 26 | /* 27 | * 저장소를 만드는 부분은 28 | * org.apache.catalina.loader.StandardClassLoader의 addRepository 메소드에서 차용 29 | */ 30 | String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString(); 31 | 32 | /* 33 | * URL을 만드는 부분은 34 | * org.apache.catalina.startup.ClassLoaderFactory의 createClassLoader 메소드에서 차용 35 | */ 36 | urls[0] = new URL(null, repository, streamHandler); 37 | loader = new URLClassLoader(urls); 38 | } catch(IOException e) { 39 | System.out.println(e.toString()); 40 | } 41 | 42 | Class myClass = null; 43 | try { 44 | myClass = loader.loadClass(servletName); 45 | } catch(ClassNotFoundException e) { 46 | System.out.println(e.toString()); 47 | } 48 | 49 | Servlet servlet = null; 50 | 51 | try { 52 | servlet = (Servlet) myClass.newInstance(); 53 | servlet.service((ServletRequest) request, (ServletResponse) response); 54 | } catch(Exception e) { 55 | System.out.println(e.toString()); 56 | } catch(Throwable e) { 57 | System.out.println(e.toString()); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/ServletProcessor2.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.net.URLClassLoader; 7 | import java.net.URLStreamHandler; 8 | 9 | import javax.servlet.Servlet; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | 13 | // 서블릿의 HTTP 요청을 처리하는 역할을 한다. 14 | public class ServletProcessor2 { 15 | 16 | public void process(Request request, Response response) { 17 | String uri = request.getUri(); 18 | String servletName = uri.substring(uri.lastIndexOf("/") + 1); 19 | URLClassLoader loader = null; 20 | try { 21 | // URLClassLoader의 생성 22 | URL[] urls = new URL[1]; 23 | URLStreamHandler streamHandler = null; 24 | File classPath = new File(Constants.WEB_ROOT); 25 | 26 | /* 27 | * 저장소를 만드는 부분은 28 | * org.apache.catalina.loader.StandardClassLoader의 addRepository 메소드에서 차용 29 | */ 30 | String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString(); 31 | 32 | /* 33 | * URL을 만드는 부분은 34 | * org.apache.catalina.startup.ClassLoaderFactory의 createClassLoader 메소드에서 차용 35 | */ 36 | urls[0] = new URL(null, repository, streamHandler); 37 | loader = new URLClassLoader(urls); 38 | } catch(IOException e) { 39 | System.out.println(e.toString()); 40 | } 41 | 42 | Class myClass = null; 43 | try { 44 | myClass = loader.loadClass(servletName); 45 | } catch(ClassNotFoundException e) { 46 | System.out.println(e.toString()); 47 | } 48 | 49 | Servlet servlet = null; 50 | // facade를 이용하여 service를 한다. 51 | RequestFacade requestFacade = new RequestFacade(request); 52 | ResponseFacade responseFacade = new ResponseFacade(response); 53 | try { 54 | servlet = (Servlet) myClass.newInstance(); 55 | servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade); 56 | } catch(Exception e) { 57 | System.out.println(e.toString()); 58 | } catch(Throwable e) { 59 | System.out.println(e.toString()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/YangDonghwa/example/ch02/StaticResourceProcessor.java: -------------------------------------------------------------------------------- 1 | package ch02; 2 | 3 | import java.io.IOException; 4 | 5 | // 정적 자원에 대한 요청을 처리하는 클래스 6 | public class StaticResourceProcessor { 7 | 8 | public void process(Request request, Response response) { 9 | try { 10 | response.sendStaticResource(); 11 | } catch(IOException e) { 12 | e.printStackTrace(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/00. 들어가기.md: -------------------------------------------------------------------------------- 1 | # 들어가기 2 | ### 작성일 3 | 2019.05.07. 4 | 5 | 6 | ## 서블릿 컨테이너의 작동 원리 7 | - 요청 객체(request object) 생성 후, 호출된 서블릿이 사용할 파라미터, 헤더, 쿠기, 질의문, URI와 같은 정보를 요청 객체에 채운다. 8 | - 서블릿이 웹 클라이언트에 응답을 보낼 때 사용할 응답 객체(response object)를 생성한다. 9 | - 서블릿의 service 메소드에 요청 객체와 응답 객체를 전달해 호출한다. 여기서 서블릿은 요청 객체로부터 값을 읽어들이고 응답 객체에 값을 쓴다. 10 | 11 | 12 | ## 카탈리나의 구성도 13 | 카탈리나는 크게 커넥터와 컨테이너라는 2개의 주요 모듈로 구성되어 있다. 14 | 15 | \- | 커넥터(Connector) | 컨테이너(Container) 16 | --- | --- | --- 17 | 설명 | 요청을 컨테이너에 연결해 주는 모듈 | 웹 서버의 내부에서 서블릿을 실행하기 위한 실행 환경을 제공하는 것. 대표적으로 Tomcat, 웹로직, Resin 등이 있다. 18 | 역할 | 전달 받은 각 HTTP 요청에 대해 요청 객체와 응답 객체를 구성한 후, 컨테이너에 전달 | 커넥터로부터 요청 및 응답 객체를 전달받고, 서블릿의 service 메소드를 호출할 책임을 진다. 19 | 20 | 21 | ## 톰캣 버전 비교 22 | 책에서 다루는 4, 5버전과 차이가 크다고 알려진 7버전, 많이 사용되는 8버전에 대해 각각 비교해보았다. 23 | 24 | 버전 | 4 | 5 | 7 | 8 25 | --- | --- | --- | --- | --- 26 | 서블릿 사양 | 2.4 | 2.5 | 3.0 | 3.1 27 | JSP 사양 | 1.2 | 2.0 | 2.2 | 2.3 28 | EL 사양 | N/A | N/A | 2.1 | 3.0 29 | WebSocket 사양 | N/A | N/A | 1.0 | 1.0 30 | 최소 Java 버전 | 1.3 | 1.4 | 1.6 (WebSocket 1.0 1.7 필요) | 1.7 31 | 32 | - EL: Java Bean의 property와 묵시적 객체에 대한 데이터 접근을 간단하게 하기 위해 만들어진 표현언어(Expression Language) 33 | - ${expression} 34 | 35 | 36 | ## 각 장의 소개 37 | 1. 간단한 HTTP 서버 38 | 2. 간단한 서블릿 컨테이너 39 | 3. 간단한 커넥터 40 | 4. 톰캣 4의 기본 커넥터 41 | 5. 컨테이너 모듈(engine, host, context, wrapper) 42 | 6. LifeCycle 인터페이스 (카탈리나 컴포넌트의 생명 주기) 43 | 7. 로거(logger) - 에러 등 메시지 기록하는 컴포넌트 44 | 8. 로더(loader) - 웹 애플리케이션이 사용하는 서블릿과 클래스 로드하는 컴포넌트 45 | 9. 매니저 - 사용자의 세션을 관리하는 컴포넌트 46 | 10. 웹 애플리케이션의 보안 - 접근 제어, 보안 주체, 보안 역할, 로그인 설정, 인증자 등 47 | 11. `org.apache.catalina.core.StandardWrapper` 클래스 자세히 살펴보기. 필터와 서블릿의 service 메소드 호출되는 원리 알아보기 48 | 12. `org.apache.catalina.core.StandardContext` 클래스 살펴보기. StandardContext 객체를 설정하는 방법, 각 HTTP 요청이 들어왔을 때 일어나는 일, 자동 재로딩의 원리, 관련 컴포넌트에서 정기적인 작업을 수행하는 스레드를 어떻게 톰캣 5가 공유하고 사용하는지 알아보기 49 | 13. host, engine 컨테이너 소개 50 | 14. 서버 컴포넌트(서블릿 컨테이너 전체의 세련된 시작/중지 방법 제공)와 서비스 컴포넌트(하나의 컨테이너와 하나 이상의 커넥터를 보유하는 컴포넌트). 서버와 서비스 사용법 51 | 15. 다이제스터(Digester) 라이브러리를 통해 웹 애플리케이션의 설정이 어떻게 이뤄지는지. (XML 문서를 자바 객체로 변환해 주는 기능을 하는데, 어떻게 변환할 수 있는지 알아본다.) 52 | 16. 사용자가 톰캣을 종료할 때 필요한 뒤처리(clean-up)을 항상 할 수 있게 하는 종료 후크(shutdown hook) 53 | 17. 배치 파일(batch file)이나 쉘 스크립트(shell script)를 써서 톰캣을 시작/중지 하는 방법 54 | 18. 배치자(deployer) - 웹 애플리케이션을 배치, 설치하는 역할을 맡음 55 | 19. ContainerServlet - 서블릿이 카탈리나의 내부 객체에 접근할 수 있게 해주는 인터페이스 / 배치된 애플리케이션을 관리할 수 있는 관리자 애플리케이션(manager application)에 관하여 56 | 20. JMX에 관해서, 톰캣이 MBean을 만들어서 자신의 내부 객체를 쉽게 관리하는 방법에 대하여 알아보기 57 | 58 | 59 | ### 참고 문헌 60 | - 톰캣 최종분석 61 | - [아파치 톰캣 버전](http://adminid.kr/apache/50534) 62 | - [[IT/트랜드] Apache Tomcat 활용하기 ① (Tomcat 설치하기)](https://daitso.kbhub.co.kr/60434/) 63 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/01. 간단한 웹 서버.md: -------------------------------------------------------------------------------- 1 | # 1. 간단한 웹 서버 2 | ### 작성일 3 | 2019.05.07. 4 | 5 | 6 | ## Hypertext Transfer Protocol(HTTP) 7 | - 인터넷에서 웹 서버와 브라우저가 데이터를 주고받는 데 필요한 프로토콜 8 | - 요청/응답 방식 9 | - 신뢰성 높은 TCP 연결 사용, 포트 80번 기본 사용 10 | 11 | ### HTTP 버전 비교 12 | 버전 | 0.9 | 1.0 | 1.1 | 2.0 13 | --- | --- | --- | --- | --- 14 | 설명 | 원-라인 프로토콜 | 확장성 만들기 | 표준 프로토콜 | 더 나은 성능을 위한 프로토콜 15 | 요청 | 단일 라인의 요청, 리소스에 대한 경로로 가능한 메서드는 GET이 유일 | 버전 정보가 각 요청 사이내로 전송되기 시작함 | 파이프라이닝을 추가하여, 첫 번째 요청에 대한 응답이 완전히 전송되기 이전에 두 번째 요청 전송을 가능케 하여, 커뮤니케이션 지연 시간(latency)을 낮춤 | 병렬 요청이 동일한 커넥션 상에서 다루어질 수 있는 다중화 프로토콜로, 순서를 제거해주고 HTTP/1.x 프로토콜의 제약사항을 막아줌
전송된 데이터의 분명한 중복과 그런 데이터로부터 유발된 불필요한 오버헤드를 제거하면서, 연속된 요청 사이의 매우 유사한 내용으로 존재하는 헤더들을 압축시킨다. 16 | 응답 | 헤더의 존재 없이 HTML 파일만 전송됨 | 헤더가 생김. 상태 코드 라인이 응답의 시작 부분에 붙어 전송됨: 브라우저가 요청의 결과에 대한 동작을 할 수 있게 됨 | Host 헤더로 인해, 동일 IP 주소에 다른 도메인을 호스트하는 기능이 서버 코로케이션을 가능케 한다. | 서버로 하여금 사전에 클라이언트 캐시를 서버 푸쉬라고 불리는 메커니즘에 의해, 필요하게 될 데이터로 채워넣도록 허용한다. 17 | 추가된 기능 | . | 헤더의 존재로, HTML 파일들 외에 다른 문서들을 전송하는 기능이 추가됨(Content-Type) | 언어, 인코딩 혹은 타입을 포함한 컨텐츠 협상이 도입되어, 클라이언트와 서버로 하여금 교환하려는 가장 적합한 컨텐츠에 대한 동의를 가능하게 함
커넥션이 재사용될 수 있게 하여, 웹페이지 탐색 시간을 절약함 | 텍스트 프로토콜이라기보다는 이진 프로토콜로, 더 이상 읽을 수도 없고 수작업을 만들어낼 수 없다. 이런 결점에 대한 보상으로 새로운 최적화 기술이 구현될 수 있다. 18 | 19 | 20 | ## HTTP 요청 21 | - 구성 22 | - 메소드 - URI(Uniform Resource Identifier) - 프로토콜/버전 23 | - 요청 헤더(Request header): 클라이언트 환경에 대한 유용한 정보(브라우저에 설정되어 있는 언어, 문서 본체의 길이 등)를 포함한다. 각각의 개별 헤더 정보는 캐리지 리턴과 개행 문자(CRLF)로 구분된다. 24 | - 헤더와 본체 사이에는 빈 줄이 하나 존재하는데, 이것이 헤더와 본체를 구분한다. 25 | - 문서 본체(Entity body) 26 | 27 | 28 | ## HTTP 응답 29 | - 구성 30 | - 프로토콜 - 상태 코드(Status code) - 설명(description) 31 | - 응답 헤더(Response header): 요청 헤더와 마찬가지로 클라이언트에게 유용한 정보를 포함한다. 32 | - 여기에도 헤더와 본체 사이에 빈 줄을 삽입하여 둘을 구분한다. 33 | - 문서 본체(Entity body): HTML 컨텐트 자체 34 | 35 | 36 | ## Socket 클래스 37 | - 소켓: 네트워크 연결에 있어서의 끝점(endpoint)에 해당하며, 애플리케이션은 소켓을 사용해 네트워크에 데이터를 읽거나 쓸 수 있다. 38 | - 소프트웨어 애플리케이션은 네트워크 연결을 통해 바이트 스트림(byte stream)을 전송하고 수신함으로써 서로간에 통신이 가능하다. 39 | - 메시지를 전달하려면 상대방 컴퓨터의 **IP**와 상대방 애플리케이션 소켓이 사용하는 **포트 번호**를 알아야 한다. 40 | - Java - `java.net.Socket`: 어디에서든 원격 서버 애플리케이션에 연결할 수 있는 '클라이언트' 소켓을 나타낸다. 41 | 42 | 43 | ## ServerSocket 클래스 44 | - 서버 애플리케이션의 경우, 클라이언트가 언제 접속을 시도할지 알 수 없는 상태에서 항상 대기하고 있어야 한다. 45 | - Java - `java.net.ServerSocket` 46 | - 클라이언트의 연결 요청을 기다리다가 연결 요청을 받아들이면, 서버 소켓을 Socket 인스턴스를 생성해 클라이언트와의 통신을 처리하게 한다. 47 | - 리스닝 할 IP 주소(=바인딩 주소(`java.net.InetAddress`)), 포트 번호, 백로그(연결 요청의 최대 큐 길이(=수락할 수 있는 연결 요청의 최대 개수)) 48 | 49 | 50 | ### 참고 문헌 51 | - 톰캣 최종분석 52 | - [HTTP의 진화](https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/Evolution_of_HTTP) 53 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/02. 간단한 서블릿 컨테이너.md: -------------------------------------------------------------------------------- 1 | # 2. 간단한 서블릿 컨테이너 2 | ### 작성일 3 | 2019.05.07. 4 | 5 | 6 | ## javax.servlet.Servlet 인터페이스 7 | 서블릿 프로그래밍은 `javax.servlet` 패키지와 `javax.servlet.http` 패키지의 클래스와 인터페이스를 통해 이뤄진다. 이들 클래스와 인터페이스 가운데 `javax.servlet.Servlet`은 가장 중요한 인터페이스로, 모든 서블릿은 이 인터페이스를 구현(implement)하거나 이 인터페이스를 구현한 클래스를 확장(extend)해야 한다. 8 | 9 | ### Servlet 인터페이스의 5가지 메소드 10 | 1. init 11 | - 생명주기와 관련된 메소드. 서블릿 클래스를 인스턴스화한 뒤, 서블릿 컨테이너가 호출하는 메소드이다. 서블릿이 어떤 요청을 받기 전에 성공적으로 끝나야 한다. 12 | 2. service 13 | - 생명주기와 관련된 메소드. 서블릿의 요청이 있을 때마다 호출되는 메소드이다. 서블릿의 생명주기 동안 자주 호출된다. 14 | 3. destroy 15 | - 생명주기와 관련된 메소드. 서블릿 컨테이너가 서비스 영역으로부터 서블릿 인스턴스를 제거하기 앞서 해당 서블릿의 destroy 메소드를 호출한다. 서블릿의 service 메소드로부터 모든 스레드가 빠져나간 뒤나 제한시간이 지난 뒤에 호출된다. 서블릿이 소유하고 있던 메모리, 파일 핸들, 스레드 등과 같은 자원을 깨끗하게 반환할 수 있는 기회를 제공하며, 모든 영속적인 상태가 메모리상의 현재 서블릿의 상태와 확실히 동기화할 수 있게끔 한다. 16 | 4. getServletConfig 17 | 5. getServletInfo 18 | 19 | 20 | ## 애플리케이션 1 21 | 소스는 `/ch02/`에 있다. 22 | - 작동시키기에 앞서 `javax.servlet.jar`를 다운로드해야 한다. 다운로드 하는 방법은 [링크](https://zetawiki.com/wiki/Javax.servlet.jar_%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C)를 참고하자. 23 | 24 | 25 | ### 참고 문헌 26 | - 톰캣 최종분석 27 | - [ZETAWIKI - Javax.servlet.jar 다운로드](https://zetawiki.com/wiki/Javax.servlet.jar_%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C) 28 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/03. 커넥터.md: -------------------------------------------------------------------------------- 1 | # 3. 커넥터 2 | ### 작성일 3 | 2019.05.08. 4 | 5 | ## 이전 장과의 비교 6 | - 2장의 애플리케이션과는 달리, 3장의 애플리케이션은 커넥터와 컨테이너가 분리되어 있다. 7 | - HttpConnector 인스턴스가 HTTP 요청을 기다리고, HttpProcessor가 요청 및 응답 객체를 생성하는 역할을 한다. 8 | - 2장) HttpServer가 HTTP 요청을 기다리고 요청/응답 객체를 생성함 9 | 10 | ## StringManager 클래스 11 | ### 톰캣의 에러 메시지 처리 12 | - 어떤 비정상적인 상황이 발생하더라도 시스템 관리자가 정확히 찾아내도록 에러 메시지를 로그 파일에 기록한다. 13 | - 또, 발생한 `javax.servlet.ServletException`의 내부에 특정 에러메시지를 기록함으로써, 서블릿 프로그래머가 자신이 작성한 서블릿에 어떤 문제가 발생했는지 알 수 있게 한다. 14 | - 프로퍼티(properties) 파일에 저장된 에러 메시지를 사용하기 때문에 에러 메시지의 편집이 용이하다. 15 | - 수백 여개의 각각의 클래스에서 사용할 에러 메시지를 하나의 프로퍼티 파일에 저장할 경우 관리상의 어려움이 발생한다. 16 | - 이를 해결하기 위해 프로퍼티 파일을 각 패키지에 배치했다. 17 | 18 | ### StringManager 클래스란? 19 | - 각 프로퍼티 파일은 `org.apache.catalina.util.StringManager` 클래스의 인스턴스가 처리한다. 20 | - 구동 시 특정 패키지마다 존재하는 프로퍼티 파일을 읽기 위해 StringManager 인스턴스가 여러 개 생성된다. 21 | - 어떤 패키지의 클래스가 그 패키지 안의 프로퍼티 파일에서 어떤 에러 메시지를 읽어야 할 때는 먼저 StringManager 인스턴스를 얻어와야 한다. 22 | - 만약 동일한 패키지의 많은 클래스가 각각 StringManager의 인스턴스를 생성해 사용한다면 자원 낭비가 발생한다. 23 | - StringManager 클래스는 동일한 패키지 안의 모든 클래스가 공유해 소용하도록 설계됐다. - Singleton pattern 24 | 25 | 26 | ## 애플리케이션 27 | 구성 모듈 | connector | startup | core 28 | --- | --- | --- | --- 29 | 클래스 | 커넥터 및 보조 클래스(HttpConnector, HttpProcessor)
HTTP 요청에 해당하는 클래스(HttpRequest)와 보조 클래스
HTTP 응답에 해당하는 클래스(HttpResponse)와 보조 클래스
퍼사드 클래스(HttpRequestFacade 및 HttpResponseFacade)
Constant 클래스 | Bootstrap: 애플리케이션 시작 시 사용됨 | ServletProcessor
StaticResourceProcessor 30 | 31 | ### HTTP 요청을 파싱하여 HttpRequest 객체에 값 채우기 32 | - 소켓의 입력 스트림 읽기 33 | - 요청 라인의 파싱 34 | - 헤더의 파싱 35 | - 쿠키의 파싱 36 | - 파라미터 얻기 37 | 38 | 39 | ## 요약 40 | - 톰캣 4의 기본 커넥터의 간단한 버전 41 | - 비효율적인 커넥터 42 | - 서블릿에서 그다지 필요로 하지 않는 HTTP 요청 헤더까지 모두 파싱 43 | - 속도 빠르지 않음 44 | - Coyote라는 더 나은 성능의 커넥터로 대체됨 45 | 46 | 47 | ### 참고 문헌 48 | - 톰캣 최종분석 49 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/04. 톰캣의 기본 커넥터.md: -------------------------------------------------------------------------------- 1 | # 4. 톰캣의 기본 커넥터 2 | ### 작성일 3 | 2019.05.08. 4 | 5 | ## 이전 장과의 비교 6 | - 기본 커넥터에 최적화 기법 추가 7 | - 다양한 객체를 위한 풀(pool)을 제공함으로써 과도한 객체 생성을 방지 8 | - 코드 내의 다양한 곳에서 문자열(string) 대신 문자의 배열(array)을 사용 9 | - HTTP 0.9, HTTP 1.0뿐 아니라 HTTP 1.1의 새로운 기능까지 모두 지원 10 | 11 | ### 커넥터 12 | - 서블릿 컨테이너에 플러그인 될 수 있는 독립적인 모듈 13 | - `org.apache.catalina.Container` 인터페이스의 invoke 메소드를 통해 요청/응답 객체를 컨테이너로 전달한다. 14 | 15 | #### 톰캣 커넥터의 요구 조건 16 | - `org.apache.catalina.Connector` 인터페이스 구현 17 | - `org.apache.catalina.Request` 인터페이스를 구현하는 요청 객체 생성 18 | - `org.apache.catalina.Response` 인터페이스를 구현하는 응답 객체 생성 19 | 20 | ### 컨테이너 21 | - invoke 메소드 내에서 서블릿 클래스의 로드, service 메소드 호출, 세션 관리, 에러 메시지의 로길 등 다양한 일을 한다. 22 | 23 | 24 | ## HTTP 1.1의 새로운 특징 25 | ### 지속 연결 26 | - HTTP 1.1 이전 27 | - 웹 서버가 요청 받은 자원을 브라우저에게 전송하면 즉시 연결을 닫도록 함: 브라우저가 페이지 하나를 요청하더라도 그 페이지에서 참조하는 다른 자원도 다운로드해야 할 필요가 있음 28 | - 페이지와 그 밖의 자원에 각자 연결해서 다운로드할 경우 속도 지연 문제 발생 29 | - 이를 해결하기 위한 방식이 **지속 연결(persistent connections)** 30 | - 서버는 페이지가 다운로드 되자마자 연결을 닫는 것이 아니라, 웹 클라이언트가 페이지에서 필요로 하는 다른 자원들을 요청할 때까지 기다린다. 31 | - 장점: 웹 서버, 클라이언트, 네트워크 부하를 모두 줄일 수 있다. 32 | 33 | ### 청크 인코딩 34 | - 청크 인코딩이란 덩어리(Chunk)의 나열로 데이터를 전송한다는 것으로, HTTP 헤더의 `Content-Length` 대신 `Transfer-Encoding`을 사용한다. 35 | - 수신자에 대한 응답을 전송하기 전에 전송하는 측은 컨텐츠의 길이를 알 필요가 없으므로, **수신하는 측은 그 컨텐츠의 전체 크기를 알기 전에 동적으로 생성한 컨텐츠를 전송할 수 있다.** 36 | 37 | ### 100(Continue) 상태 코드의 사용 38 | - HTTP 1.1 클라이언트는 `Expect: 100-continue` 헤더를 전송해, 요청 본체를 전송하기에 앞서 서버 측의 승인을 기다릴 수 있다. 39 | - 서버가 요청을 수신할 의지가 있다면 응답헤더에 `100-continue`를 포함에 클라이언트에게 응답한다. 40 | 41 | 42 | ## Connector 인터페이스 43 | - Connector와 Container는 일대일(one-to-one) 관계 44 | - HttpConnector와 HttpProcessor는 일대다(one-to-many) 관계 45 | 46 | ### 주요 메소드 47 | - getContainter 48 | - 이 커넥터와 연결된 컨테이너 반환 49 | - setContainer 50 | - 커넥터와 컨테이너를 연결 51 | - createRequest 52 | - 들어오는 HTTP 요청에 대한 요청 객체 생성 53 | - createResponse 54 | - 응답 객체 생성 55 | 56 | 57 | ## HttpConnector 클래스 58 | - HttpProcessor 인스턴스는 HTTP 요청 라인과 헤더를 파싱하고 요청 객체를 만들어야 하는 책임이 있다. 또한, 각 HttpProcessor는 요청/응답 객체와 연관되어있으므로, HttpProcessor의 생성자 내에서 HttpConnector 클래스의 createRequest와 createResponse 메소드를 호출한다. 59 | 60 | ### 구현해야 할 요소 61 | - `org.apache.catalina.Connector`: 카탈리나에서 적절히 동작하기 위함 62 | - `java.lang.Runnable`: 인스턴스 자신이 스레드로서 실행하기 위함 63 | - `org.apache.catalina.Lifecycle`: 카탈리나 컴포넌트의 생명주기를 관리하기 위함 64 | - Lifecycle 구현 시, HttpConnector 인스턴스를 생성한 뒤 반드시 initialize와 start 메소드를 호출해야 한다. 이 두 메소드는 컴포넌트의 생명주기 동안 오직 한 번만 호출해야 한다. 65 | 66 | ### 서버 소켓의 생성 67 | HttpConnector의 initialize 메소드는 자신의 open 메소드를 호출해 `java.net.ServerSocket` 인스턴스를 리턴받고, 이를 `serverSocket` 레퍼런스에 할당한다. 68 | open 메소드에서는 `java.net.ServerSocket`의 생성자를 호출하는 대신 서버 소켓의 팩토리로부터 `ServerSocket` 인스턴스를 얻는다. 69 | 70 | ### HttpProcessor 인스턴스의 관리 71 | 기본 커넥터에서 HttpProcessor는 HttpProcessor 객체의 풀을 가지며, 각 HttpProcessor 인스턴스는 자신의 스레드를 가지므로, 동시에 여러 HTTP 요청을 처리할 수 있다. 72 | 73 | 74 | ## 요청 처리 75 | ### process 메소드 동작 76 | - 연결 정보의 파싱 77 | - 요청의 파싱 78 | - 헤더의 파싱 79 | 80 | 81 | ### 참고 문헌 82 | - 톰캣 최종분석 83 | - [Chunked Response란?](https://sunghwanjo.tistory.com/entry/Chunked-Response%EB%9E%80) -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/05. 컨테이너.md: -------------------------------------------------------------------------------- 1 | # 5. 컨테이너 2 | ### 작성일 3 | 2019.05.10. 4 | 5 | ## 컨테이너란? 6 | - 서블릿 요청을 처리하고 웹 클라이언트에 보낼 응답 객체를 만들어 주는 컴포넌트 7 | - 인터페이스: `org.apache.catalina.Container` 8 | - 종류: Engine, Host, Context, Wrapper 9 | 10 | 11 | ## Container 인터페이스 12 | 카탈리나의 컨테이너는 각기 다른 개념적 개념에 따라 네 종류의 컨테이너가 존재한다. 13 | 14 | 컨테이너 | 개념 15 | --- | --- 16 | Engine | 카탈리나 서블릿 엔진 전체를 나타낸다. 17 | Host | 다양한 컨텍스트에 따른 가상 호스트를 나타낸다. 18 | Context | 웹 애플리케이션을 나타내며, 둘 이상의 래퍼를 가진다. 19 | Wrapper | 각각의 서블릿을 나타낸다. 20 | 21 | 위의 각각의 개념적인 단계는 `org.apache.catalina` 패키지의 `Engine, Host, Context, Wrapper` 인터페이스로 표현된다. 이 인터페이스는 모두 `Container` 인터페이스를 확장한다. 22 | 23 | 제대로 작동하는 카탈리나를 만들 때 네 종류의 컨테이너가 모두 필요하지는 않고, 각 컨테이너에는 하위 단계의 컨테이너가 없을 수도 있고 반대로 하위 컨테이너가 많이 있을 수도 있다. 24 | 25 | Container 인터페이스의 메소드 | 기능 26 | --- | --- 27 | addChild | 하위 컨테이너 추가 28 | removeChild | 하위 컨테이너 제거 29 | findChild
findChildren | 각각의 하위 컨테이너나 하위 컨테이너의 집합을 찾을 수 있도록 제공하는 메소드 30 | 31 | 컨테이너는 Loader, Logger, Manager, Realm, Resources와 같은 다양한 지원 컴포넌트를 가질 수 있으며, Container 인터페이스는 자신이 포함하고 있는 컴포넌트에 관한 get 메소드와 set 메소드를 제공한다. 32 | 33 | Container 인터페이스는 톰캣 관리자가 설정 파일(server.xml)을 편집함으로써 컨테이너의 동작 방식을 배치(deployment) 시에 결정할 수 있게 설계되었다. 이는 파이프라인과 한 세트의 밸브를 도입함으로써 가능해졌다. 34 | 35 | 36 | ## 파이프라인 태스크 37 | 파이프라인은 컨테이너가 호출할 태스크들을 포함하며 하나의 특정한 태스크는 밸브 하나로 포함된다. 컨테이너의 파이프라인에는 하나의 기본 밸브(Basic Valve)가 존재하지만 원하는 밸브를 얼마든지 추가할 수 있다. 밸브 수는 기본 밸브를 제외하고 추가한 밸브의 수로 정의되며, 밸브는 톰캣의 설정 파일(server.xml)을 편집함으로써 동적으로 추가할 수 있다. 38 | 39 | 밸브는 전달받은 요청/응답 객체를 조작할 수 있다. 하나의 밸브가 처리 작업을 끝내면 파이프라인에서의 다음 밸브를 호출하게 된다. 기본 밸브는 항상 마지막에 호출된다. 40 | 41 | 하나의 컨테이너는 하나의 파이프라인을 가질 수 있다. 컨테이너의 invoke 메소드가 호출되면 컨테이너는 파이프라인으로 처리권을 넘기며, 파이프라인은 첫 번째 밸브를 호출한다. 첫 번째 밸브에서의 처리가 완료되면 다음 밸브가 호출되며, 파이프라인에 더 이상의 밸브가 없을 때까지 호출이 진행된다. 42 | 43 | ### Pipeline 인터페이스 44 | 45 | 메소드 | 기능 46 | --- | --- 47 | invoke | 컨테이너가 파이프라인 내의 모든 밸브를 호출할 때 처음 호출하는 메소드 48 | addValve | 새로운 밸브 추가 49 | removeValve | 기존 밸브 제거 50 | setBasic | 기본 밸브(Basic Valve) 할당 51 | getBasic | 기본 밸브 얻어오기 52 | 53 | - 가장 마지막에 호출되는 기본 밸브는 요청 객체와 그에 대응하는 응답 객체를 처리하는 책임을 가진다. 54 | 55 | ### Valve 인터페이스 56 | 밸브는 요청의 처리에 대한 책임이 있는 컴포넌트이며, Valve 인터페이스는 하나의 밸브를 나타낸다. 57 | 58 | 메소드 | 기능 59 | --- | --- 60 | invoke | 전달받은 요청/응답 객체 조작 61 | getInfo | Valve를 구현한 객체에 관한 정보 반환 62 | 63 | ### ValveContext 인터페이스 64 | 65 | 메소드 | 기능 66 | --- | --- 67 | invokeNext | 다음 밸브 invoke 68 | getInfo | ValveContext를 구현한 객체 정보를 반환 69 | 70 | ### Contained 인터페이스 71 | 밸브 클래스는 선택적으로 `org.apache.catalina.Contained` 인터페이스를 구현할 수도 있다. 이 인터페이스는 구현 클래스가 최대한 하나의 컨테이너와 연결돼있다는 것을 나타낸다. 72 | 73 | 74 | ## Wrapper 인터페이스 75 | 래퍼란 개별적인 하나의 서블릿을 대변하는 컨테이너이다. Wrapper의 구현 클래스는 각각의 서블릿의 생명주기를 책임져야 한다(서블릿의 init, service, destroy 메소드 호출 등). 래퍼는 가장 하위 단계의 컨테이너이므로 하위 컨테이너를 추가할 수 없다. 76 | 77 | 메소드 | 기능 78 | --- | --- 79 | allocate | 초기 서블릿 인스턴스를 래퍼에 할당한다. 또한, 서블릿이 `javax.servlet.SingleThreadModel` 인터페이스를 구현하는지를 반드시 고려해야 한다. 80 | load | 래퍼가 대변하는 서블릿 인스턴스를 로드하고 초기화한다. 81 | 82 | 83 | ## Context 인터페이스 84 | 컨텍스트는 웹 애플리케이션을 나타내는 컨테이너이다. 컨테이너는 하위 컨테이너로서 보통 둘 이상의 래퍼를 갖고 있다. 85 | 86 | ## 컨테이너의 처리 순서 87 | 1. 하나의 컨테이너는 하나의 파이프라인을 갖는다. 컨테이너의 invoke 메소드는 파이프라인의 invoke 메소드를 호출한다. 88 | 2. 파이프라인의 invoke 메소드는 컨테이너에 추가된 모든 밸브를 호출한 다음 기본 밸브의 invoke 메소드를 호출한다. 89 | 3. 래퍼의 기본 밸브는 관련된 서블릿 클래스를 로드하고 요청에 대한 응답을 할 책임을 진다. 90 | 4. 컨텍스트의 기본 밸브는 요청을 처리하는 책임을 가지는 하위 컨테이너를 찾기 위해 맵퍼를 이용한다. 하위 컨테이너를 찾은 후에는 그 컨테이너의 invoke 메소드를 호출한다. 그 다음 다시 1번의 단계로 돌아간다. 91 | 92 | 93 | ## 요약 94 | 컨테이너는 커넥터 다음으로 중요한 모듈로, 로더, 로거, 매니저와 같은 여러 다른 모듈들을 사용한다. 컨테이너의 종류로는 엔진, 호스트, 컨텍스트, 래퍼가 있고 카탈리나는 이 4개의 컨테이너를 모두 필요로 하지는 않는다. 95 | 96 | 97 | ### 참고 문헌 98 | - 톰캣 최종분석 99 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/06. 생명주기.md: -------------------------------------------------------------------------------- 1 | # 6. 생명주기 2 | ### 작성일 3 | 2019.05.12. 4 | 5 | ## 들어가기 전에 6 | 카탈리나는 다양한 컴포넌트로 이뤄져 있다. 카탈리나가 기동될 때 이러한 컴포넌트도 함께 기동돼야 하며, 카탈리나가 종료될 때 컴포넌트도 깨끗이 종료되어야 한다. 컴포넌트의 일관된 시작/종료 메커니즘은 `org.apache.catalina.Lifecycle` 인터페이스를 구현함으로써 가능하다. 7 | 8 | Lifecycle 인터페이스를 구현한 컴포넌트는 다양한 이벤트를 하나 이상 발생시킬 수 있다. 9 | 10 | 이벤트 | 발생하는 상황 11 | --- | --- 12 | `BEFORE_START_EVENT`
`START_EVENT`
`AFTER_START_EVENT` | 컴포넌트 시작할 때 13 | `BEFORE_STOP_EVENT`
`STOP_EVENT`
`AFTER_STOP_EVENT` | 컴포넌트 종료할 때 14 | 15 | 이에 따른 이벤트 리스너는 `org.apache.catalina.LifecycleListener` 인터페이스로 나타낸다. 16 | 17 | 18 | ## Lifecycle 인터페이스 19 | 카탈리나는 컴포넌트가 다른 컴포넌트를 포함할 수 있게 설계되었다. 상위 컴포넌트는 하위 컴포넌트의 시작과 종료에 대한 책임이 있다. 카탈리나의 설계 방식에서는 모든 컴포넌트들이 상위 컴포넌트에 붙잡혀 있게 되기 때문에, 부트스트랩 클래스는 오직 하나의 컴포넌트만 시작하면 된다. 이러한 단일 시작/종료 메커니즘은 Lifecycle 인터페이스를 통해 가능하다. 20 | 21 | 메소드 | 기능 22 | --- | --- 23 | start | 상위 컴포넌트가 해당 컴포넌트를 시작할 수 있게 함 24 | stop | 상위 컴포넌트가 해당 컴포넌트를 종료할 수 있게 함 25 | addLifecycleListener
findLifecycleListener
removeLifecycleListener | 컴포넌트는 어떤 이벤트에 대응하는 리스너를 가질 수 있는데, 이벤트가 발생되면 그 이벤트에 대응하는 리스너가 통보를 받을 것이다. 이 세 메소드는 리스너와 관련된 기능을 한다. 26 | 27 | Lifecycle 인스턴스가 발생시킬 수 있는 이벤트는 Lifecycle 인터페이스 내에 문자열 상수로 정의되어 있다. 이벤트에 대한 내용은 `들어가기 전에`에 표로 정리해두었다. 28 | 29 | 30 | ## LifecycleEvent 클래스 31 | `org.apache.catalina.LifecycleEvent`는 생명주기 이벤트를 나타내는 클래스이다. 32 | 33 | 34 | ## LifecycleListener 인터페이스 35 | `org.apache.catalina.LifecycleListener`는 생명주기 리스너를 나타내는 인터페이스이다. 이 인터페이스에는 하나의 메소드만 존재한다. 이 메소드는 리스너가 기다리고 있는 이벤트가 발생할 때 호출되는 메소드이다. 36 | 37 | ## LifecycleSupport 클래스 38 | Lifecycle을 구현하며 각 이벤트에 리스너를 등록할 수 있는 컴포넌트라면 반드시 리스너와 관련된 3개의 메소드(`addLifecycleListener, findLifecycleListener, removeLifecycleListener`)를 구현해야 한다. 39 | 40 | 또한, 모든 리스너는 해당 컴포넌트 내에 배열이나 ArrayList, 아니면 그와 비슷한 형태의 객체에 저장되어야 한다. 41 | 42 | 카탈리나는 컴포넌트가 리스너를 다루는 방법과 생명주기 이벤트를 발생시키는 방법을 도와주는 `org.apache.catalina.util.LifecycleSupport`라는 유틸리티 클래스를 제공한다. 43 | 44 | 메소드 | 동작 45 | --- | --- 46 | addLifecycleListener | 하나의 리스너를 추가할 때에는 기존에 있던 배열의 요소 개수에 하나를 더한 수만큼의 크기로 새로운 배열이 생성된다. 그 다음에 기존 배열의 모든 요소가 새로운 배열에 복사되고 그에 더해 새 리스너가 추가된다. 47 | removeLifecycleListener | 하나의 리스너가 제거될 때에는 기존에 있던 배열의 요소 개수에서 하나를 뺀 수만큼의 크기를 갖는 새로운 배열이 생성되며, 제거하려는 리스너를 제외한 모든 요소들이 새 배열로 복사된다. 48 | fireLifecycleListener | 하나의 생명주기 이벤트를 발생시킨다. 먼저 listeners 배열을 복제한 후, 각 요소들의 lifecycleEvent 메소드에 이벤트를 전달해 호출한다. 49 | 50 | Lifecycle을 구현하는 컴포넌트는 LifecycleSupport 클래스를 사용할 수 있다. 51 | 52 | 53 | ## 요약 54 | Lifecycle 인터페이스는 컴포넌트의 생명주기를 정의하고 다른 컴포넌트에게 이벤트를 전달하는 세련된 방법을 제공한다. 또한 Lifecycle 인터페이스는 한 번의 시작/종료 명령으로 카탈리나 전체 컴포넌트의 시작/종료를 가능하게 한다. 55 | 56 | 57 | ### 참고 문헌 58 | - 톰캣 최종분석 59 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/07. 로거.md: -------------------------------------------------------------------------------- 1 | # 7. 로거 2 | ### 작성일 3 | 2019.05.13. 4 | 5 | ## 들어가기 전에 6 | 로거는 메시지를 기록해주는 컴포넌트이다. 카탈리나에서의 로거는 컨테이너와 연결돼있으며, 타 컴포넌트에 비해 상대적으로 간단하다. 톰캣은 `org.apache.catalina.Logger` 패키지에서 다양한 로거들을 제공하고 있다. 7 | 8 | 9 | ## Logger 인터페이스 10 | 모든 로거는 반드시 `org.apache.catalina.Logger` 인터페이스를 구현해야 한다. 11 | 12 | Logger 인터페이스는 이 인터페이스를 구현하는 클래스에서 선택할 수 있는 다양한 종류의 log 메소드를 정의해놓고 있다. 13 | 14 | 메소드 | 기능 15 | --- | --- 16 | log | 단순히 문자열(메시지)을 전달하는 로그부터, 클래스의 인스턴스에 지정된 출력 수준(verbosity level)에 미치지 못하는 값을 전달할 경우 메시지가 기록되는 로그도 있다. 17 | getVervosity
setVervosity | 출력 수준을 얻거나 설정하기 18 | getContainer
setContainer | 연결되어 있는 컴포넌트를 얻거나 설정하기 19 | addPropertyChangeListener
removePropertyChangeListener | PropertyChangeListener를 추가하거나 제거하기 위함 20 | 21 | 22 | ## 톰캣의 로거 23 | 톰캣은 `org.apache.catalina.logger` 패키지에 있으며, `org.apache.catalina.logger.LoggerBase` 클래스를 확장하고 있는 3개의 로거(FileLogger, SystemErrLogger, SystemOutLogger)를 제공한다. 24 | 25 | 톰캣 버전 | 로거 구성 26 | --- | --- 27 | 4 | LoggerBase 클래스가 `org.apache.catalina.Logger` 인터페이스를 구현함 28 | 5 | LoggerBase 클래스가 추가로 Lifecycle과 MBeanRegistration 인터페이스를 구현함 29 | 30 | ### LoggerBase 클래스 31 | 여기서는 톰캣 4에서의 LoggerBase를 살펴본다. (톰캣 5의 LoggerBase에서는 MBean을 생성하는 코드와 결합되어 있기 때문에 복잡하다.) 32 | 33 | 톰캣 4에서의 LoggerBase 클래스는 log 메소드를 제외한 Logger 인터페이스의 모든 메소드를 구현해놓은 추상 클래스이다. 34 | 35 | `public abstract void log(String msg)` 36 | - 하위 클래스의 로깅을 수행하는 메소드이다. 다른 모든 log 메소드는 이 메소드를 호출하게 된다. 각각의 하위 클래스는 서로 다른 대상에 메시지를 기록할 수 있기 때문에, 이 메소드는 LoggerBase 클래스에서 구현되어 있지 않다. 37 | - 출력 수준의 경우, vervosity라는 protected 변수를 갖고 있다. 38 | - `FATAL, ERROR, WARNING, INFORMATION, DEBUG` 중 하나의 값을 setVerbosity 메소드에 넘기면 출력 수준을 설정할 수 있다. 39 | 40 | LoggerBase는 하위 클래스로 `SystemOutLogger, SystemErrLogger, FileLogger` 클래스를 가진다. 41 | 42 | #### SystemOutLogger 클래스 43 | log 메소드를 구현하고 있는데, 이 메소드가 전달 받은 모든 메시지를 System.out.println 메소드로 다시 전달한다. 44 | 45 | #### SystemErrLogger 클래스 46 | log 메소드의 인자로 들어가는 메시지가 `System.err.println()` 메소드로 전달되는 것을 제외하고는 SystemOutLogger 클래스와 동일하다. 47 | 48 | #### FileLogger 클래스 49 | FileLogger는 LoggerBase의 하위 클래스 중 가장 진보된 로거이다. 50 | 51 | 이 클래스는 연결되어 있는 컨테이너로부터 메시지를 받아 파일에 기록하는데, 각 메시지 별로 타임스탬프를 같이 기록할 수도 있다. 또, 이 클래스는 로그 파일의 이름에 접두어나 접미어를 붙일 수 있는 방법도 제공한다. 52 | 53 | 톰캣 4에서의 FileLogger 클래스는 Lifecycle 인터페이스를 구현하기 때문에 `org.apache.catalina.Lifecycle`을 구현하는 다른 컴포넌트와 마찬가지로 시작/중지될 수 있다. 톰캣 5에서의 LoggerBase 클래스가 Lifecycle을 구현하며 FileLogger 클래스는 LoggerBase를 확장한다. 54 | 55 | 톰캣 4의 LoggerBase 클래스에서 구현한 Lifecycle 인터페이스의 start, stop 메소드에서는 파일 로거를 시작하고 중지시킬 때 생명주기 이벤트를 발생시키는 것 이상의 일을 하지는 않는다. stop 메소드는 로그 파일을 닫기 위해 close 메소드를 호출한다. 56 | 57 | FileLogger 인스턴스의 생명주기 동안 log 메소드는 여러 개의 파일을 열고 닫을 수 있다. 일반적으로 log 메소드는 날짜가 바뀐 경우 현재의 파일을 닫고 새 파일을 여는 방식으로 로그 파일을 전환한다. 58 | 59 | 메소드 | 설명 60 | --- | --- 61 | open | 로그 파일을 생성하려는 디렉토리가 이미 존재하는지 먼저 확인하고, 디렉토리가 존재하지 않으면 새 디렉토리를 만든다. 그 다음 `java.io.PrintWriter` 인스턴스를 생성하는데, 이 인스턴스는 `java.io.FileWriter` 객체를 사용해 해당하는 파일에 쓰기 작업을 수행한다. 생성된 PrintWrter 인스턴스는 writer라는 변수에 할당되어 log 메소드에서 메시지를 기록할 때 사용된다. 62 | close | writer 버퍼의 내용을 스트림에 쓰게 하고, PrintWriter를 닫은 후 null을 할당한다. 63 | log | 현재의 날짜와 시간을 쉽게 알아내기 위해 `java.sql.Timestamp` 클래스의 인스턴스를 생성하는 것으로 시작한다. 날짜가 바뀐 경우, 현재의 로그 파일을 닫고 새로운 로그 파일을 연다.
PrintWriter 인스턴스를 통해 로그를 기록하며, 이 PrintWriter의 출력 스트림은 로그 파일로 지정되어 있다. 필요하다면 타임스탬프를 붙여 로그에 기록한다. 64 | 65 | 66 | ## 요약 67 | 로거 컴포넌트에 대한 내용, `org.apache.catalina.Logger` 인터페이스, 톰캣이 갖고 있는 3개의 Logger 구현 클래스(SystemOutLogger, SystemErrLogger, FileLogger)를 살펴봤다. 68 | 69 | 70 | ### 참고 문헌 71 | - 톰캣 최종분석 72 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/08. 로더.md: -------------------------------------------------------------------------------- 1 | # 8. 로더 2 | ### 작성일 3 | 2019.05.14. 4 | 5 | ## 들어가기 전에 6 | 서블릿 컨테이너는 필요한 기능을 갖춘 별도의 전용 로더를 사용해야 하며, 시스템의 클래스 로더를 그대로 사용하면 안된다. 7 | 8 | 시스템의 클래스 로더를 사용할 경우, 서블릿이 운영중인 자바 가상 머신(JVM)의 CLASSPATH 변수에 잡혀있는 어떤 클래스나 라이브러리에 접근하는 것이 가능해지므로, **보안 문제** 가 발생할 수 있다. 서블릿은 오직 `WEB-INF/classes` 디렉토리 이하의 클래스와 `WEB-INF/lib` 디렉토리 내에 있는 라이브러리만 사용해야 한다. 9 | 10 | 또 다른 이유는 `WEB-INF/classes`나 `WEB-INF/lib` 디렉토리에 있는 클래스가 변경될 때마다 **자동으로 재로딩하는 기능** 을 지원해야 하기 때문이다. 톰캣의 로더를 구현하는 클래스 로더는 서블릿과 기타 클래스 파일들의 타임스탬프를 지속적으로 확인하기 위해 별도의 스레드를 사용한다. 11 | 12 | ### 인터페이스 13 | 인터페이스 | 기능 14 | --- | --- 15 | `org.apache.catalina.Loader` | 로더 인터페이스 16 | `org.apache.catalina.loader.Reloader` | 자동 재로딩 지원하기 위해 구현해야 하는 인터페이스 17 | 18 | ### 주 용어 19 | - 저장소(repository) 20 | - 클래스 로더가 찾아야 할 장소 21 | - 자원(resource) 22 | - 컨텍스트 문서 루트에 대한 정보를 갖고 있는 클래스 로더의 DirContext 객체 23 | 24 | 25 | ## 자바 클래스 로더 26 | 자바 클래스의 인스턴스를 생성할 때마다 해당 클래스는 메모리에 로드된다. JVM은 클래스를 로드할 때 클래스 로더를 사용한다. 대부분의 클래스 로더는 자바의 핵심 라이브러리와 CLASSPATH 환경변수에 잡혀 있는 모든 라이브러리를 찾는다. 그러나 필요한 클래스를 찾지 못했을 경우엔 `java.lang.ClassNotFoundException`을 발생시킨다. 27 | 28 | J2SE 1.2부터 JVM에는 **부트스트랩 클래스 로더, 확장 클래스 로더, 시스템 클래스 로더** 라고 하는 클래스 로더가 생겼다. 이 3개의 클래스 로더는 모두 계층 관계를 이루고 있는데, 가장 상위에 있는 것은 부트스트랩 클래스 로더, 가장 하위에 있는 것은 시스템 클래스 로더이다. 29 | 30 | 로더 이름 | 설명 31 | --- | --- 32 | 부트스트랩 클래스 로더
(bootstrap class loader) | JVM 시작 시 사용된다.
JVM이 올바르게 작동하려면 필요한 클래스를 초기에 로드해야 하므로, 원시 코드(native code)를 구현해야 한다.
`java.lang`이나 `java.io`패키지 등에 속하는 자바의 모든 핵심 클래스를 로드할 책임이 있는데, 이를 위해 rt.jar나 i18n.jar와 같은 핵심 라이브러리를 찾는다. 33 | 확장 클래스 로더
(extension class loader) | 표준 확장 디렉토리에 있는 클래스들을 로드하는 역할을 한다.
프로그래머가 필요한 JAR 파일을 확장 디렉토리에 복사해 두면, 확장 클래스 로더가 이 JAR 파일을 자동으로 찾는다. 34 | 시스템 클래스 로더
(system class loader) | 애플리케이션이 사용하는 클래스를 로드한다. 35 | 36 | ### 위임 모델(delegation model) 37 | 클래스 로더 사용에 있어 보안 상의 이유로 사용하는 모델이다. 38 | 39 | 클래스가 로드되어야 할 때는 항상 시스템 클래스 로더가 먼저 호출되지만, 시스템 클래스 로더는 곧바로 해당 클래스를 로드하지 않는다. 대신 이 임무를 부모 클래스 로더인 확장 클래스 로더에 위임하며, 확장 클래스 로더는 다시 부모 클래스 로더인 부트스트랩 클래스 로더에 위임한다. 따라서 가장 먼저 어떤 클래스를 로드할 수 있는 기회를 맞는 클래스 로더는 항상 **부트스트랩 클래스 로더** 가 된다. 40 | 41 | 최상위 클래스 로더가 해당 클래스를 찾지 못했다면 하위 클래스 로더가 클래스를 찾는 식으로 진행되고, 최종적으로 시스템 클래스 로더까지 클래스를 찾지 못한다면 `java.lang.ClassNotFoundException`을 던지게 된다. 42 | 43 | #### 사용하는 이유 44 | 핵심 라이브러리의 `java.lang.Object`와 누군가가 악의적으로 작성한 `java.lang.Object`클래스가 있을 때, 위임 모델을 사용하면 최상위 클래스 로더부터 클래스를 찾아 인스턴스를 생성시키므로, 누군가가 악의적으로 작성한 `java.lang.Object`는 로드되지 못한다. 45 | 46 | #### 자바의 클래스 로딩 메커니즘에서 얻을 수 있는 장점 47 | `java.lang.ClassLoader`라는 추상 클래스를 확장하면 얼마든지 나름대로의 클래스 로더를 만들 수 있다. 톰캣이 자신만의 로더를 필요로 하는 이유는 다음과 같다. 48 | - 클래스 로드시 특정한 규칙을 적용해야 할 때 49 | - 이미 로드한 클래스를 캐싱(caching)해야 할 때 50 | - 즉시 사용할 수 있게 클래스를 미리 로드해놔야 할 때 51 | 52 | 53 | ## Loader 인터페이스 54 | 웹 애플리케이션에서 서블릿이나 그 밖의 클래스를 로딩하는 데는 규칙이 존재한다. 55 | - 웹 애플리케이션에서의 서블릿은 **WEB-INF/classes** 이하의 디렉토리에 존재하는 클래스 이외의 다른 클래스에는 접근할 수 없어야 한다. 56 | - 서블릿은 **WEB-INF/lib** 디렉토리에 있는 라이브러리 이외의 다른 라이브러리에는 접근할 수 없어야 한다. 57 | 58 | 톰캣의 로더는 클래스 로더가 아니라 웹 애플리케이션 로더라고 봐야 하고, 하나의 로더는 반드시 `org.apache.catalina.Loader` 인터페이스를 구현해야 한다. 로더 구현 클래스는 `org.apache.catalina.loader.WebappClassLoader` 클래스로 대변되는 별도의 클래스 로더를 사용한다. 하나의 톰캣 로더는 보통 하나의 컨텍스트와 연결되어 있다. 59 | 60 | 메소드 이름 | 기능 61 | --- | --- 62 | `getClassLoader` | 웹 로더에 있는 ClassLoader를 얻는다. 63 | `getContainter`
`setContainer` | 컨텍스트와 연결된 로더를 얻어내거나, 로더를 컨텍스트와 연결한다. 64 | `getDefaultContext`
`setDefaultContext` | . 65 | `getDelegate`
`setDelegate` | 상위 클래스 로더에게 클래스 로드하는 일을 지정한다. 66 | `getInfo` | 67 | `getReloadable`
`setReloadable` | 로더에 재로딩 기능을 설정하거나 설정 여부를 확인한다. 68 | `addPropertyChangeListener` | 69 | `addRepository` | 저장소를 추가한다. 70 | `findRepositories` | 모든 저장소의 배열을 반환한다. 71 | `modified` | 자동 재로딩 기능을 지원하기 위한 메소드. 저장소에서 하나 이상의 클래스가 변경됐을 경우 true를 반환하여 재로딩이 필요하다는 사실을 나타내야 한다. 72 | `removePropertyChangeListener` | 73 | 74 | 카탈리나는 Loader 인터페이스를 구현한 `org.apache.catalina.loader.WebappLoader` 클래스를 제공한다. 75 | > 로더와 연결되어 있는 컨테이너는 서블릿 클래스가 필요할 때마다(= 컨테이너의 invoke 메소드가 호출될 때마다) 먼저 로더의 getClassLoader 메소드를 호출한다. 그리고 컨테이너는 서블릿 클래스를 로드하기 위해 클래스 로더의 loadClass 메소드를 호출한다. 76 | 77 | 78 | ## Reloader 인터페이스 79 | 클래스 로더는 자동 재로딩 기능을 지원하기 위해 `org.apache.catalina.loader.Reloader` 인터페이스를 구현해야 한다. 80 | 81 | 메소드 이름 | 기능 82 | --- | --- 83 | addRepository | 84 | findRepositories | Reloader를 구현하는 클래스 로더의 모든 저장소를 String의 배열로 반환한다. 85 | **modified** | 웹 어플리케이션 내의 서블릿이나 클래스가 하나라도 변경됐다면 modified 메소드는 true를 반환한다. 86 | 87 | 88 | ## WebappLoader 클래스 89 | 구현하는 인터페이스 | 설명 90 | --- | --- 91 | `org.apache.catalina.Loader` | 필요한 클래스를 로드할 책임이 있는 웹 애플리케이션 로더를 구현한다. 92 | `org.apache.catalina.Lifecycle` | 다른 카탈리나의 컴포넌트와 마찬가지로 생명주기 인터페이스를 구현하므로 연결된 컨테이너에 의해 시작/중지된다. 93 | `java.lang.Runnable` | 클래스 로더의 modified 메소드를 계속 호출하는 하나의 스레드로서 실행된다.
클래스의 재로딩 자체는 WebappLoader가 아닌 Context가 수행한다. 94 | 95 | ### WebappLoader의 start 메소드 호출 시 수행되는 작업 96 | - 클래스 로더 생성 97 | - 저장소 설정 98 | - 클래스 경로 설정 99 | - 자원에 대한 접근 권한 설정 100 | - 재로딩을 위한 새로운 스레드 시작 101 | 102 | ### 클래스 로더의 생성 103 | Loader에는 setClassLoader 메소드가 없지만, WebappLoader는 private 변수인 loaderClass의 값을 얻거나 설정하기 위한 getLoaderClass와 setClassLoader 메소드를 제공한다. 104 | 105 | loaderClass는 클래스 로더에 해당하는 클래스 이름을 나타내는 String 타입의 변수이며, 기본값은 `org.apache.catalina.loader.WebappClassLoader`이다. 원하면 WebappClassLoader를 확장하는 클래스 로더를 만들어서 setClassLoader 메소드에 전달해, WebappLoader가 그 클래스 로더를 사용하게 할 수 있다. 106 | 107 | 별도의 클래스 로더를 사용하는 경우가 아니라면 WebappLoader는 시작 시 **createClassLoader** 메소드를 호출해 WebappClassLoader 인스턴스를 생성한다. WebappClassLoader 이외의 다른 클래스 로더를 사용할 수 있지만, createClassLoader 메소드는 WebappClassLoader를 반환하고 있음에 주의한다. 108 | 109 | ### 저장소의 설정 110 | WebappLoader 클래스의 start 메소드는 **setRepositories** 메소드를 호출해 클래스 로더에 저장소를 호출한다. WEB-INF/classes 디렉토리는 클래스 로더의 addRepository 메소드로 전달되며, WEB-INF/lib 디렉토리는 클래스 로더의 setJarPath 메소드로 전달된다. 이렇게 함으로써 클래스 로더는 지정된 디렉토리에 있는 클래스와 라이브러리를 로드할 수 있게 된다. 111 | 112 | ### 클래스 경로의 설정 113 | start 메소드에서 setClassPath 메소드를 호출함으로써 수행되는 기능이다. setClassPath 메소드는 재스퍼(Jasper) JSP 컴파일러를 위해 클래스 경로 정보를 포함하는 문자열을 서블릿 컨텍스트의 속성에 설정한다. 114 | 115 | ### 접근 권한의 설정 116 | 톰캣이 운영할 때 보안 관리자를 사용하고 있다면, setPermissions 메소드는 WEB-INF/lib과 WEB-INF/classes 같은 필수 디렉토리에의 접근 권한을 클래스 로더에게 부여한다. 117 | 118 | ### 자동 재로딩을 위한 새로운 스레드의 시작 119 | WEB-INF/classes나 WEB-INF/lib 디렉토리에 있는 클래스가 다시 컴파일되면, 톰캣을 다시 시작하지 않아도 그 클래스는 자동으로 다시 로드돼야 한다. WebappLoader는 이를 위해 각 자원의 타임스탬프를 x초마다 확인하는 스레드를 가지고 있다. 120 | 121 | WebappLoader는 자동 재로딩을 지원하기 위해 `java.lang.Runnable` 인터페이스를 구현한다. run 메소드의 주요 동작은 다음과 같다. 122 | 1. checkInterval 변수에 설정된 시간만큼 대기한다. 123 | 2. WebappLoader가 사용하는 클래스 로더의 modified 메소드를 호출함으로써 로드된 클래스가 변경됐는지를 확인한다. 변경되지 않았다면 계속 진행한다. 124 | 3. 클래스가 변경됐다면 notifyContext 메소드를 호출해, 이 WebappLoader와 연결된 Context가 재로딩을 수행하도록 한다. 125 | 126 | 127 | ## WebappClassLoader 클래스 128 | `org.apache.catalina.loader.WebappClassLoader`클래스는 웹 애플리케이션이 사용하는 클래스들을 로드해야하는 클래스 로더를 대변한다. WebappClassLoader는 최적화 및 보안을 염두하고 설계되었다. 129 | 130 | 보안 때문에 일부러 로드하지 않는 클래스나 패키지를 변수에 저장해두며, 캐싱에 대해서는 후술하겠다. 131 | 132 | ### 캐싱 133 | 더 나은 성능을 내기 위해 이미 로드된 클래스를 캐싱함으로써, 다음 요청이 있을 때 캐시로부터 곧바로 클래스를 사용하는 것이 가능하다. 캐싱 작업은 지역적으로 이루어진다.(= WebappClassLoader 인스턴스가 캐시를 관리한다.) 134 | 135 | `java.lang.ClassLoader`는 이전에 로드되어있던 클래스들을 Vector에 저장하여 관리함으로써 이 클래스들을 가비지 컬렉션이 되지 않도록 한다. 이 경우 캐시는 상위 클래스에 의해 관리된다. 136 | 137 | 캐시된 모든 자원들은 resourceEntries라고 하는 HashMap에 저장되며, 자원의 이름이 key가 된다. 찾을 수 없는 자원은 모두 notFoundException라고 하는 HashMap에 별도로 저장된다. 138 | 139 | ### 클래스의 로딩 140 | WebappClassLoader는 다음과 같은 규칙에 따라 클래스를 로드한다. 141 | 1. 이전에 로드된 클래스는 모두 캐시되어있으므로, 해당 클래스가 지역 캐시에 있는지 먼저 확인 142 | 2. 지역 캐시에서 찾지 못한 경우 `java.lang.ClassLoader`의 findLoadedClass를 호출해 클래스 로더에 캐시되어 있는지 확인 143 | 3. 1과 2에서 찾지 못했다면 웹 애플리케이션이 재정의한 J2EE 클래스를 사용하지 못하도록 시스템 클래스 로더를 사용 144 | 4. SecurityManager가 사용된다면 클래스를 로드해도 되는지 확인. 로드되어서는 안 되는 클래스라면 ClassNotFoundException 발생시킴 145 | 5. 위임 플래그가 켜 있더나 로드할 클래스의 패키지가 packgeTriggers에 포함되어 있다면 클래스를 로드하기 위해 상위 클래스 로더 사용. 상위 클래스 로더가 null일 경우 시스템 클래스 로더 사용 146 | 6. 현재의 저장소로부터 클래스 로드 147 | 7. 현재의 저장소에서 클래스를 찾을 수 없고 위임 플래그가 꺼져 있을 경우 상위 클래스 로더 사용. 상위 클래스 로더가 null이라면 시스템 클래스 로더 사용 148 | 8. 그래도 클래스를 찾을 수 없다면 ClassNotFoundException 던짐 149 | 150 | 151 | ## 요약 152 | 웹 애플리케이션에서 클래스를 로드하기 위해 내부적으로 클래스 로더를 사용한다. 이 내부 클래스 로더는 톰캣을 위한 클래스로 만들어졌으며, 애플리케이션 컨텍스트 내에서 클래스를 로드하는 데 필요한 규칙을 준수한다. 또한 캐싱 기능을 지원하며, 자동 재로딩을 지원하기 위해 하나 이상의 클래스가 변경됐는지를 확인할 수 있다. 153 | 154 | 155 | ### 참고 문헌 156 | - 톰캣 최종분석 157 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/09. 세션 관리.md: -------------------------------------------------------------------------------- 1 | # 9. 세션 관리 2 | ### 작성일 3 | 2019.05.15. 4 | 5 | ## 세션 6 | ### Session 인터페이스 7 | Session 인터페이스는 카탈리나의 내부 퍼사드로서 작동한다. Session 인터페이스의 표준 구현 클래스는 StandardSession이며, 이 클래스는 `javax.servlet.http.HttpSession` 인터페이스를 구현한다. 8 | 9 | 주 메소드들의 이름과 기능은 다음과 같다. Session 객체는 언제나 매니저와 연결되어 있다는 점에 유의하며 기능들을 살펴보자. 10 | 11 | 메소드 이름 | 기능 12 | --- | --- 13 | setManager
getmanager | Session 객체를 매니저에 연결한다. 14 | setId
getId | Session 객체는 매니저와 연결되어 있는 컨텍스트 내에서 유일한 식별자를 갖는데, 세션의 식별자를 접근할 때 사용한다. 15 | getLastAccessedTime | Session 객체의 유효성을 확인하기 위해 매니저에 의해 호출된다. 16 | setValid | 매니저가 세션의 유효성을 설정하거나 재설정 할 때 호출한다. 17 | access | 누군가 Session 객체를 접근할 때마다 마지막으로 접근한 시간을 갱신하기 위해 호출된다. 18 | expire | 매니저가 세션을 만료시키기 위해 호출한다. 19 | getSession | 이 퍼사드로 매핑되어 있는 HttpSession 객체를 얻기 위해 사용한다. 20 | 21 | ### StandardSession 클래스 22 | StandardSession 클래스는 Session 인터페이스의 표준 구현 클래스이다. `javax.servlet.http.HttpSession`과 `org.apache.catalina.Session` 인터페이스를 구현하는 것 외에도 `java.lang.Serializable` 인터페이스를 구현함으로써 Session 객체가 직렬화가 가능하게 한다. 23 | 24 | 생성자 메소드에서는 Manager 인스턴스를 받음으로써 Session 객체가 항상 Manager를 갖고 있게 한다. 25 | 26 | StandardSession을 이루는 변수들은 모두 각각의 get 메소드와 set 메소드를 가지고 있다. 톰캣 5에서는 모두 protected로 선언되어 있지만, 톰캣 4에서는 private로 선언되어 있다. 일부 변수는 직렬화하는 것을 방지하기 위해 transient 키워드를 사용한다. 27 | 28 | 메소드 이름 | 기능 29 | --- | --- 30 | getSession | 현재의 인스턴스를 전달해 StandardSessionFacade 객체를 반환한다. 31 | expire | Session 인터페이스의 expire 메소드이다.
매니저의 maxInactiveInterval 변수에 지정된 시간 동안 한 번도 접근된 적이 없는 Session 객체를 만료시킬 때 사용한다. 32 | 33 | ### StandardSessionFacade 클래스 34 | 서블릿에 Session 객체를 전달하기 위해 카탈리나는 StandardSession 인스턴스를 생성하고, 내용을 완성한 후, 이 인스턴스를 서블릿에 전달할 수 있지만, `javax.servlet.http.HttpSession` 메소드만을 구현하고 있는 StandardSession의 인스턴스를 전달한다. 35 | 36 | 이렇게 함으로써 서블릿 프로그래머가 사용해서는 안 되는 StandardSession의 메소드를 호출하는 것을 방지할 수 있다. 37 | 38 | 39 | ## 매니저 40 | ### 매니저란? 41 | - 세션 객체 관리 42 | - 세션 객체를 생성하거나 무효화할 수 있음 43 | - `org.apache.catalina.Manager` 인터페이스로 대변됨 44 | - `org.apache.catalina.session` 패키지에는 매니저에서 제공해야 하는 공통적인 기능을 구현한 ManagerBase 클래스가 포함되어 있다. ManagerBase를 상속받는 2개의 하위 클래스는 다음과 같다. 45 | 46 | 클래스 이름 | 설명 47 | --- | --- 48 | StandardManager | 기동 시 세션 객체를 메모리에 저장한다.
중지될 때에는 현재 메모리에 존재하는 모든 세션 객체를 파일로 저장한다.
다시 기동될 때에는 파일로부터 세션 객체를 다시 로드한다. 49 | PersistentManagerBase | 세션 객체를 보조기억장치에 저장하는 역할을 하는 매니저 컴포넌트의 기본 클래스
이를 확장하는 클래스로는 PersistentManager와 DistributeManager(톰캣 4에만 존재)가 있다. 50 | 51 | ### Manager 인터페이스 52 | Manager 인터페이스는 매니저 컴포넌트를 대변한다. 주요 메소드는 다음과 같다. 53 | 54 | 메소드 이름 | 기능 55 | --- | --- 56 | setContainer
getContainer | 매니저를 컨텍스트에 연결한다. 57 | createSession | Session 객체를 생성한다. 58 | add | Session 인스턴스를 세션 풀에 추가한다. 59 | remove | 세션 풀에서부터 Session 객체를 제거한다. 60 | getMaxInactiveInterval
setMaxInactiveInterval | 매니저가 사용자의 세션을 제거할 때까지 대기하는 시간을 얻거나 지정한다. 61 | load | 저장되어있던 세션 객체를 다시 메모리에 올린다. 62 | unload | 현재 활성화되어 있는 세션을 매니저가 지정한 저장장치에 저장한다. 63 | 64 | ### ManagerBase 클래스 65 | ManagerBase 클래스는 모든 매니저 클래스들이 확장해야 하는 추상 클래스로서, 하위 클래스를 위한 공통적인 기능을 제공한다. 66 | 67 | 하나의 컨텍스트를 위한 Manager 인스턴스는 컨텍스트 내의 모든 활성 세션(active session: 만료되지 않은 유효한 세션 객체)을 관리해야 하므로, 활성 세션들은 sessions라는 이름의 HashMap에 저장된다. 68 | 69 | 메소드 이름 | 기능 70 | --- | --- 71 | createSession | [Manager 인터페이스 참조] 72 | generateSessionId | 해당 세션의 유일한 식별자 반환 73 | add | Session 객체를 sessions에 추가한다. 74 | remove | session로부터 Session 객체를 제거한다. 75 | findSession(파라미터 없음) | sessions로부터 모든 활성 세션을 Session 인스턴스의 배열의 형태로 반환한다. 76 | findSession(파라미터 있음) | 파라미터로는 세션 식별자가 들어가며, 식별자에 해당하는 Session 인스턴스를 반환한다. 77 | 78 | ### StandardManager 클래스 79 | StandardManager 클래스는 Manager 인터페이스의 표준 구현 클래스이며, 세션 객체를 메모리에 저장하는 역할을 한다. 또한, Lifecycle 인터페이스를 구현해 시작/중지될 수 있다. 매니저는 더 이상 유효하지 않은 세션 객체를 제거하는 역할도 한다. 주요 메소드는 다음과 같다. 80 | 81 | 메소드 이름 | 기능 82 | --- | --- 83 | stop | unload 메소드를 호출함으로써 유효한 Session 인스턴스들을 SESSIONS.ser이라는 파일에 저장하게 된다.
StandardManager가 다시 기동되면 load 메소드가 호출되어 Session 객체들이 다시 메모리에 로드된다. 84 | processExpires | 루프를 돌면서, StandardManager가 관리하는 모든 Session 인스턴스의 lastAccessedTime의 값을 현재 시각과 비교하는 루프를 수행한다.
lastAccessedTime과 현재 시각과의 차이가 maxInactiveInterval의 값을 초과하면 expire 메소드를 호출하여 Session 인스턴스를 만료시킨다.
`org.apache.catalina.core.StandardContext`의 sessionTimeOut 변수의 기본값은 30분이다. 85 | 86 | #### 톰캣 5에서의 StandardManager 클래스(차이점) 87 | - `java.lang.Runnable`을 구현하지 않음 88 | - processExpires 메소드는 backgroundProcess 메소드(톰캣 4에는 없음)에 의해 직접적으로 호출됨 89 | 90 | ### PersistentManagerBase 클래스 91 | - 모든 지속 매니저(persistent manager)의 상위 클래스 92 | - StandardManager와 다른 점은 **저장 장치(세션 객체가 저장될 보조기억장치)의 사용 여부** 93 | - 세션 객체는 백업(back-up)될 수도 있고 스왑 아웃(swap-out)될 수도 있다. 94 | 95 | 세션 객체의
동작 | 설명 96 | --- | --- 97 | 백업 | 세션 객체가 저장장치에 **복사** 되며, 원래의 세션 객체는 **여전히 메모리에 남는다**.
따라서 서버에 이상이 생겼을 경우 활성 세션 객체를 저장장치로부터 다시 읽어올 수 있다. 98 | 스왑 아웃 | 세션 객체가 저장장치로 **이동** 된다.
이는 세션 객체가 너무 오랫동안 사용되지 않았거나 활성 세션 객체의 수가 지정된 세션의 수를 초과했기 때문이다.
**메모리 공간 절약** 을 위해 사용된다. 99 | 100 | - 톰캣 4에서는 `java.lang.Runnable` 인터페이스를 구현함으로써, 정기적으로 활성 세션을 백업하거나 스왑 아웃시키는 별도의 스레드가 실행된다. 101 | - 톰캣 5에서는 `java.lang.Runnable`을 구현하지 않고, 백업과 스왑 아웃 작업은 backgroundProcess 메소드에 의해 수행된다. 이 메소드는 연결되어 있는 StandardContext 인스턴스가 정기적으로 호출한다. 102 | 103 | #### 스왑 아웃 104 | PersistentManagerBase 클래스는 세션 객체를 스왑 아웃시키는 데 있어 여러 가지 규칙을 적용한다. 105 | - 세션 객체의 수가 maxActiveSessions 변수의 값을 초과하는 경우 106 | - 활성 세션 객체의 수가 maxActiveSessions의 값과 동일해질 때까지 단순히 아무 세션 객체나 스왑 아웃시킨다. 107 | - 세션 객체가 너무 오랫동안 사용되지 않는 경우 108 | - minIdleSwap과 maxIdleSwap 변수를 사용하여 해당 세션 객체를 스왑 아웃 시킬 것인지 결정한다. lastAccessedTime의 값이 두 변수의 값을 초과하면 이 세션 객체는 스왑 아웃된다. 109 | 110 | 활성 세션은 스왑 아웃될 가능성이 있으므로 그 세션은 메모리 또는 저장장치에 존재하게 될 것이다. 따라서 findSession 메소드는 Session 인스턴스를 먼저 메모리에서 찾아본 후, 찾지 못하면 다시 저장장치에서 찾는다. 111 | 112 | #### 백업 113 | 모든 활성 세션 객체가 백업되는 것이 아니라, maxIdleBackup의 값보다 더 오랫동안 사용되지 않은 세션 객체만을 백업한다. 세션 객체의 백업은 processMaxIdleBackups 메소드가 수행한다. 114 | 115 | ### PersistentManager 클래스 116 | PersistentManagerBase 클래스를 확장한다. 117 | 118 | ### DistributeManager 클래스 119 | DistributeManager 클래스는 PersistentManagerBase의 하위 클래스로서, 둘 이상의 노드로 구성된 클러스터 환경에서 사용된다. 하나의 노드란 하나의 톰캣 서버를 말한다. 하나의 클러스터에서 하나의 노드는 서로 다른 머신에 존재할 수도 있고 동일한 머신에 존재할 수도 있다. 120 | 121 | 클러스터 환경에서 각각의 노드는 **세션 복제(session replication)** 를 위해 반드시 DistributeManager를 매니저로서 사용해야 한다. 122 | 123 | DistributeManager는 세션 객체가 생성되거나 제거될 때마다 다른 노드에 이를 통보한다. 또한 노드는 다른 노드로부터의 통보를 수신할 수 있어야 한다. 서로 다른 노드에 존재하는 DistributeManager 인스턴스 간에 통보를 주고 받을 수 있도록, 카탈리나는 `org.apache.catalina.cluster` 패키지에 관련 클래스(ClusterSender, ClusterReceiver)를 제공한다. 124 | 125 | DistributeManager는 `java.lang.Runnable`을 구현함으로써 각각 세션 객체를 만료시키는 스레드와 다른 노드로부터 통보를 받는 스레드가 별도로 실행된다. 126 | 127 | 메소드 이름 | 기능 128 | --- | --- 129 | createSession | 현재의 인스턴스에 저장될 세션 객체를 생성시키는 것뿐만 아니라, 반드시 ClusterSender 인스턴스를 사용해 노드에 이를 통보해야 한다. 130 | processClusterReceiver | 다른 노드로부터 통보를 받은 사항을 처리한다. 131 | 132 | 133 | ## Store 인터페이스 134 | 저장장치는 `org.apache.catalina.Store` 인터페이스로 대변되며, 매니저에 의해 관리되는 세션들을 저장하기 위한 지속적인 저장장치를 제공하는 컴포넌트이다. 135 | 136 | Store 인터페이스에서 중요한 2개의 메소드는 save와 load이다. 137 | 138 | 메소드 이름 | 기능 139 | --- | --- 140 | **save** | 세션 객체를 저장 장치에 저장한다. 141 | **load** | 주어진 세션 식별자를 이용해 저장 장치로부터 세션 객체를 로드한다. 142 | keys | 모든 세션 객체의 식별자를 String의 배열로 반환한다. 143 | 144 | ### StoreBase 클래스 145 | StoreBase 클래스는 2개의 하위 클래스인 FileStore와 JDBCStore를 위해 공통 기능을 제공하는 추상 클래스이다. 이 클래스는 Store 인터페이스의 세션을 저장할 저장 장치의 종류에 따라 구현이 달라지는 save와 load 메소드를 구현하지 않는다. 146 | 147 | - 톰캣 4의 StoreBase 클래스에서는 정기적으로 세션을 만료시킬지의 여부를 확인하고 만료된 세션을 활성 세션의 집합으로부터 제거하는 역할을 맡는 별도의 스레드가 실행된다. 148 | - 톰캣 5에서는 processExpires를 호출하는 별도의 스레드가 존재하지 않는다. 그 대신 연결되어 있는 PersistentManagerBase 인스턴스의 backgroundProcess 메소드에서 processExpires 메소드를 정기적으로 호출한다. 149 | 150 | ### FileStore 클래스 151 | FileStore 클래스는 세션 객체를 파일에 저장한다. 파일의 이름은 세션 객체의 식별자와 동일하며, 여기에 .session이라는 확장자가 붙는다. 이 파일은 임시 작업 디렉토리에 위치하게 된다. 주요 메소드는 다음과 같다. 152 | 153 | 메소드 이름 | 기능 154 | --- | --- 155 | setDirectory | 파일이 저장되는 임시 디렉토리의 위치를 변경한다. 156 | save | `java.io.ObjectOutputStream` 클래스를 사용하여 세션 객체를 직렬화한다.
따라서 Session 인스턴스에 저장되는 모든 객체는 반드시 `java.lang.Serializable`을 구현해야 한다. 157 | load | `java.io.ObjectOutputStream` 클래스를 사용하여 세션 객체를 역직렬화(deserialization)한다. 158 | 159 | ### JDBCStore 클래스 160 | JDBCStore 클래스는 JDBC를 사용해 세션 객체를 데이터베이스에 저장한다. 주요 메소드는 다음과 같다. 161 | 162 | 메소드 이름 | 기능 163 | --- | --- 164 | setDriverName | JDBC 드라이버 클래스 이름 지정 165 | serConnectionURL | 연결할 데이터베이스 URL 지정 166 | 167 | 168 | ## 요약 169 | 세션을 관리하는 컴포넌트인 매니저의 종류와 매니저가 지속적으로 세션 객체를 저장하는 방법에 대해 알아보았다. 170 | 171 | 172 | ### 참고 문헌 173 | - 톰캣 최종분석 174 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/10. 보안.md: -------------------------------------------------------------------------------- 1 | # 10. 보안 2 | ### 작성일 3 | 2019.05.16. 4 | 5 | ## 들어가기 전에 6 | - 서블릿 기술은 전개 설명자(web.xml 파일)의 설정을 통해 원하는 컨텐츠에 보안 제약(security constraint)을 적용할 수 있도록 지원한다. 7 | - 서블릿 컨테이너는 인증자(authenticator)라는 밸브를 통해 보안 제약 기능을 지원한다. 8 | - 인증자 밸브는 사용자를 인증하는 밸브이며, 래퍼 밸브보다 이전에 호출된다. 인증에 성공했다면 인증자 밸브는 다음 밸브를 호출하고, 실패했다면 다음 밸브를 호출하지 않음으로써 인증에 성공한 사용자만 요청한 서블릿을 열람할 수 있도록 한다. 9 | - 인증자 밸브는 컨텍스트의 인증 영역에 존재하는 authenticate 메소드를 사용자 이름과 암호를 전달하여 호출한다. 이 영역은 유효한 사용자 정보의 집합에 접근할 수 있다. 10 | - 보안 기능과 관련된 클래스는 다음과 같다. 11 | - 인증 영역 12 | - 보안 주체 13 | - 보안 역할 14 | 15 | 16 | ## Realm 17 | ### 영역(realm) 18 | - **사용자 정보가 저장된 장소와 그에 따른 인증 방법을 구분하는 단위** 19 | - 사용자를 인증하는 데 필요한 컴포넌트이다. 20 | - 사용자 이름과 암호가 유효한지 말해준다. 21 | - **하나의 컨테이너** 는 **하나의 *영역*** 만을 가질 수 있으며, 컨테이너의 setRealm 메소드에 *영역* 객체를 전달함으로써 *영역* 을 컨테이너에 연결할 수 있다. 22 | 23 | ### 사용자를 인증하는 방법 24 | - 톰캣 25 | - 유효한 사용자의 정보를 기본적으로 tomcat-users.xml 파일에 저장한다. 26 | - 관계형 데이터베이스와 같은 다른 원천을 사용해 인증할 수 있는 *영역* 을 구현할 수 있다. 27 | 28 | ### `org.apache.catalina.Realm` 인터페이스 29 | 카탈리나에서 *영역* 은 `org.apache.catalina.Realm` 인터페이스로 대변된다. 이 인터페이스의 가장 중요한 4개의 authenticate 메소드는 다음과 같다. 30 | 31 | - public Principal authenticate(String **username**, String **credentials**) 32 | - public Principal authenticate(String **username**, byte[] **credentials**) 33 | - public Principal authenticate(String **username**, String **digest**, String **nonce**, String **nc**, String **cnonce**, String **qop**, String **realm**, String **md5a2**) 34 | - public Principal authenticate(X509Certificate **certs[]**) 35 | 36 | 주요 메소드는 다음과 같다. 37 | 38 | 메소드 이름 | 기능 39 | --- | --- 40 | authenticate | 인증한다. 41 | hasRole | 주체가 지정된 역할을 가지고 있는지 검사한다. 42 | setContainer
getContainer | *영역* 을 컨테이너에 연결한다. 43 | 44 | Realm 인터페이스를 기본적으로 구현한 것은 `org.apache.catalina.realm.RealmBase`라는 추상 클래스이며, `org.apache.catalina.realm` 패키지에는 JDBCRealm, JNDIRealm, MemoryRealm, UserDatabaseRealm 등 RealmBase를 확장한 여러 클래스들이 포함되어 있다. 기본적으로는 MemoryRealm이 사용된다. MemoryRealm이 처음 시작될 때 tomcat-users.xml 문서를 읽어 들린다. 45 | 46 | > 카탈리나의 인증자 밸브는 사용자를 인증하기 위해 연결되어 있는 *영역* 객체의 authenticate 메소드를 호출한다. 47 | 48 | 49 | ## GenericPrincipal 50 | ### 주체(principal) 51 | - 인증을 받는 실체로, 사용자나 그룹 등 인증을 받는 어떠한 대상도 '주체'로 표현될 수 있다. 52 | - `java.security.Principal` 인터페이스로 나타낸다. 53 | - 카탈리나에서 Principal의 구현 클래스는 `org.apache.catalina.realm.GenericPrincipal` 클래스이다. 54 | 55 | ### GenericPrincipal 56 | - *영역* 과 항상 연결되어 있어야 한다. 57 | - 반드시 이름과 암호를 갖고 있어야 한다. 58 | - 역할의 목록을 전달할 수 있으며, 문자열 형태의 역할을 hasRole 메소드에 전달해서 이 *주체* 가 지정된 역할을 가지고 있는지 여부를 확인할 수 있다. 59 | 60 | 61 | ## LoginConfig 62 | 하나의 로그인 설정은 하나의 *영역* 이름을 포함하며 `org.apache.catalina.deploy.LoginConfig` 클래스로 대변된다. LoginConfig 인스턴스는 *영역* 이름과 사용할 인증 방법을 캡슐화하고 있다. 63 | 64 | 메소드 이름 | 기능 65 | --- | --- 66 | getRealmName | *영역* 이름을 얻을 수 있다. 67 | getAuthName | 사용하는 인증 방법의 이름을 알 수 있다.
인증 방법은 반드시 `BASIC, DIGEST, FORM, CLIENT-CERT` 중 하나다. 68 | 69 | 폼 기반의 인증 방법이 사용된다면 LoginConfig 객체는 loginPage와 errorPage 특성 값으로 각각 로그인 페이지의 URL과 에러 페이지의 URL을 가지고 있다. 70 | 71 | ### 동작 72 | 1. 톰캣은 시작 시에 web.xml 파일을 읽어들임 73 | 2. web.xml에 login-config 요소를 포함하고 있다면, 톰캣은 LoginConfig 객체를 생성하고 그 특성을 설정함 74 | 3. 인증자 밸브는 LoginConfig의 getRealmName 메소드를 호출하고, *영역* 이름을 브라우저에 전달해 로그인 대화 상자가 나타나도록 함 75 | 4. getRealmName 메소드가 null을 반환했다면 *영역* 이름 대신에 서버 이름과 포트를 브라우저에게 전달함 76 | 77 | 78 | ## Authenticator 79 | - 인증자의 주된 역할은 **사용자를 인증하는 것** 이다. 80 | - `org.apache.catalina.Authenticator` 인터페이스는 인증자를 대변함 81 | - 아무런 메소드도 갖고 있지 않은 마커(marker) 인터페이스 82 | - 다른 컴포넌트가 어떤 컴포넌트에 대해 instanceof를 수행하여 그 컴포넌트가 인증자인지 여부를 확인할 수 있음 83 | - 카탈리나는 `org.apache.catalina.authenticator.AuthenticatorBase`라는 Authenticator의 기본 구현 클래스를 제공함 84 | - AuthenticatorBase는 Authenticator 인터페이스를 구현하는 것 외에도 `org.apache.catalina.valves.ValveBase` 클래스를 확장하고 있음 85 | - 이는 **AuthenticatorBase가 인증자인 동시에 밸브** 라는 의미 86 | 87 | ### `org.apache.catalina.authenticator` 패키지 88 | 여러 인증자 구현 클래스가 포함되어 있다. 89 | - BasicAuthenticator: 기본 인증에 사용된다. 90 | - FormAuthenticator: 폼 기반(form-based)의 인증에 사용된다. 91 | - DigestAuthenticator: 다이제스트 인증에 사용된다. 92 | - SSLAuthenticator: SSL 인증에 사용된다. 93 | - NonLoginAuthenticator: 톰캣 사용자가 auth-method 요소의 값을 지정하지 않았을 경우 사용되는 클래스로, 보안 제약 사항만을 확인하고 사용자 인증을 하지 않는 인증자를 나타낸다. 94 | 95 | 각 클래스마다 인증하는 방식이 다르기 때문에, 각 클래스들은 추상 메소드로 선언된 authenticate 메소드를 각자만의 인증 방식을 사용하도록 구현한다. 96 | 97 | 98 | ## 인증자 밸브의 설치 99 | 하나의 컨텍스트는 하나의 LoginConfig 객체만을 가질 수 있고 인증 방법을 구현한 하나의 클래스만을 사용한다. 100 | 101 | 컨텍스트 내에서 AuthenticatorBase의 어떤 하위 클래스가 인증자 밸브로서 사용되는지는 전개 설명자의 auth-method 요소의 값에 달려 있다. auth-method 요소의 값과 그에 해당하는 인증자 클래스는 다음과 같다. 102 | 103 | auth-method 요소의 값 | 인증자 클래스 104 | --- | --- 105 | BASIC | BasicAuthenticator 106 | FORM | FormAuthenticator 107 | DIGEST | DigestAuthenticator 108 | CLIENT-CERT | SSLAuthenticator 109 | 존재하지 않음(= authMethod 특성의 값이 NONE으로 간주됨) | NonLoginAuthenticator 110 | 111 | 인증자 클래스는 동적으로 로드되기 때문에, 어떤 인증자 클래스가 사용될지는 런타임 시에만 알 수 있다. 112 | 113 | StandardContext 클래스는 StandardContext 인스턴스의 다양한 설정을 위해 `org.apache.catalina.startup.ContextConfig` 클래스를 사용한다. 이러한 설정에는 인증자 클래스의 인스턴스를 생성하는 것과 이를 컨텍스트에 연결하는 작업이 포함된다. 114 | 115 | 116 | ## 요약 117 | 서블릿 프로그래밍에 있어서 보안은 중요한 주제이며, 서블릿 표준 규약은 **보안 주체**, **역할**, **보안 제약**, **로그인 설정** 등 보안과 관련한 개념을 제공함으로써 보안의 필요성을 충족하고 있다. 서블릿 컨테이너가 이러한 주제를 어떻게 실현하고 있는지 알아보았다. 118 | 119 | 120 | ### 참고 문헌 121 | - 톰캣 최종분석 122 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/11. StandardWrapper.md: -------------------------------------------------------------------------------- 1 | # 11. StandardWrapper 2 | ### 작성일 3 | 2019.05.16. 4 | 5 | ## 들어가기 전에 6 | 컨테이너에는 **엔진, 호스트, 컨텍스트, 래퍼** 의 네 종류가 있다. 7 | 8 | 보통 하나의 컨텍스트는 하나 이상의 래퍼를 가지며, 각각의 래퍼는 서블릿에 대한 정의를 나타낸다. 9 | 10 | 카탈리나에서 Wrapper 인터페이스를 구현한 표준 구현 클래스를 살펴보자. 먼저 각각의 HTTP 요청에 대해 호출되는 메소드의 순서를 알아보고, 그 다음 `javax.servlet.SingleThreamModel` 인터페이스를 설명한다. 그리고 나서 StandardWrapper와 StandardWrapperValve 클래스를 설명한다. 11 | 12 | 13 | ## 메소드 호출 순서 14 | 1. 커넥터는 StandardContext 인스턴스의 invoke 메소드 호출 15 | 2. StandardContext의 invoke 메소드는 컨텍스트 파이프라인의 invoke 메소드 호출 16 | - StandardContext의 파이프라인에서 기본 밸브는 StandardContextValve이며, 따라서 StandardContext의 파이프라인은 StandardContextValve의 invoke 메소드 호출 17 | 3. StandardContextValve의 invoke 메소드는 요청을 처리할 적합한 래퍼를 얻은 다음, 이 래퍼의 invoke 메소드 호출 18 | 4. StandardWrapper는 래퍼의 표준 구현 클래스. StandardWrapper 인스턴스의 invoke 메소드는 자신의 파이프라인의 invoke 메소드 호출 19 | 5. StandardWrapper의 파이프라인에 있는 기본 밸브는 StandardWrapperValve이며, 따라서 StandardWrapperValve의 invoke 메소드 호출 20 | - StandardWrapperValve의 invoke 메소드는 래퍼의 allocate 메소드를 호출해 서블릿의 인스턴스를 얻음 21 | 6. 서블릿을 로드할 필요가 있다면 allocate 메소드는 서블릿을 로드하기 위해 load 메소드 호출 22 | 7. load 메소드는 서블릿의 init 메소드 호출 23 | 8. StandardWrapperValve는 서블릿의 service 메소드 호출 24 | 25 | 26 | ## SingleThreadModel 27 | 서블릿은 `javax.servlet.SingleThreadModel` 인터페이스를 구현할 수 있다. 이 인터페이스를 구현한 서블릿을 보통 SingleThreadModel(STM) 서블릿이라고 부른다. 28 | 29 | ### 인터페이스 구현의 목적 30 | - 하나의 서블릿이 한 번에 하나의 요청만을 처리할 수 있게 하는 것 31 | - 서블릿이 이 인터페이스를 구현하고 있다면, 서블릿의 service 메소드에서 결코 2개 이상의 스레드가 실행되지 않는다. 32 | - 서블릿 컨테이너는 서블릿의 단일 인스턴스로의 접근을 동기화(synchronization)하거나 각각의 새로운 요청을 각 서블릿 인스턴스가 처리할 수 있도록 서블릿 인스턴스의 풀(pool)을 관리하는 방법을 사용해 이를 보장해야 한다. 33 | 34 | ### 문제점 35 | - 2개 이상의 스레드가 동시에 service 메소드를 실행하지 못하는 것은 사실이나, 서블릿 컨테이너는 성능 향상을 위해 **STM 서블릿의 인스턴스를 여러 개 생성할 수 있다.** 36 | - 즉, **STM 서블릿의 service 메소드는 서로 다른 인스턴스에서 동시에 실행될 수 있다.** 37 | - 인터페이스는 서블릿이 정적 클래스 변수나 서블릿 범위 바깥의 클래스와 같은 **공유 자원에 접근함으로써 발생하는 동기화 문제를 해결하지 못한다.** 38 | 39 | SingleThreadModel 인터페이스는 서블릿 프로그래머에게 **다중 스레드의 안정성에 대한 잘못된 개념을 전달할 수 있기 때문에** 서블릿 2.4에서 **이 인터페이스는 디프리케이트(deprecated) 되었다.** 40 | 41 | 42 | ## StandardWrapper 43 | StandardWrapper 객체의 주된 역할은 **자신이 대변하는 서블릿을 로드하고 인스턴스를 생성하는 것**이다. 하지만 이 객체가 service 메소드를 호출하는 것이 아니고, 이 일은 StandardWrapper의 파이프라인에 있는 기본 밸브인 StandardWrapperValve 객체가 담당한다. 44 | 45 | StandardWrapperValve 객체는 StandardWrapper의 allocate 메소드를 호출하여 서블릿 인스턴스를 얻는다. 서블릿 인스턴스를 받은 후, 서블릿의 service 메소드를 호출시킨다. 46 | 47 | StandardWrapper는 서블릿이 최초로 요청됐을 때 서블릿 클래스를 로드한다. 48 | 49 | 메소드 이름 | 기능 50 | --- | --- 51 | setServletClass | 서블릿 클래스의 이름을 전달해 호출함으로써 동적으로 서블릿을 로드함 52 | setName | 서블릿의 참조 이름을 전달한다. 53 | 54 | StandardWrapperValve가 요청한 서블릿 인스턴스를 메모리에 할당하는 것과 관련하여, StandardWrapper는 그 서블릿이 SingleThreadModel 인터페이스를 구현하는지의 여부를 반드시 고려해야 한다. 55 | 56 | 상황 | SingleThreadModel이 아닐 경우 | SingleThreadModel일 경우 57 | --- | --- | --- 58 | 로드 | 서블릿 클래스를 오직 한 번 로드하며, 다음 요청에 대해서는 동일한 서블릿 인스턴스를 반환한다. | StandardWrapper 인스턴스는 2개 이상의 스레드가 동시에 STM 서블릿의 service 메소드를 실행하지 않음을 반드시 보장해야 한다. 59 | 서블릿
인스턴스의
생성 | 하나 이상의 스레드가 서블릿의 service 메소드를 동시에 호출해도 안전하다고 가정하기 때문에 서블릿 인스턴스를 여러 개 생성할 필요가 없다. | 성능 향상을 위해 STM 서블릿 인스턴스들의 풀을 유지한다. 60 | 61 | **래퍼는 서블릿 내에서 얻을 수 있는 `javax.servlet.ServletConfig` 인스턴스를 준비해야 하는 책임이 있다. (서블릿의 할당과 로드)** 62 | 63 | 64 | ### 서블릿 할당 65 | StandardWrapperValve의 invoke 메소드는 **요청받은 서블릿 인스턴스를 얻기 위해 래퍼의 allocate 메소드를 호출**한다. 66 | 67 | allocate 메소드의 내용은 **STM 서블릿을 처리하는 부분**과 **일반 서블릿을 처리하는 부분**으로 나눠져 있다. 68 | 69 | #### allocate 메소드의 동작 70 | 1. instance가 null인지 확인 71 | - null이라면 loadServlet 메소드를 호출하여 서블릿을 로드 72 | 2. StandardWrapper가 나타내는 서블릿이 STM 서블릿이라면 풀로부터 인스턴스를 꺼내 반환 73 | - 지정한 최대 개수를 넘지 않는 한도에서 STM 서블릿의 인스턴스를 할당한다. 74 | - 지정한 최대 개수보다 크거나 같다면 instancePool 스택의 wait 메소드를 호출하여 인스턴스가 풀로 돌아오기를 기다리게 된다. 75 | 76 | ### 서블릿 로드 77 | StandardWrapper 클래스는 Wrapper 인터페이스의 load 메소드를 구현한다. **load 메소드는 loadServlet 메소드를 호출해 서블릿을 로드**하고, `javax.servlet.ServletConfig` 인스턴스를 서블릿의 init 메소드에 전달해 호출한다. 78 | 79 | #### loadServlet 메소드의 동작 80 | 1. 현재의 StandardWrapper가 STM 서블릿을 나타내는지 확인 81 | 2. 로드할 서블릿 클래스의 이름이 servletClass라는 멤버 변수에 할당되어 있어야 하는데, 이 변수의 값을 다시 actualClass라는 String 타입의 변수에 할당함 82 | 3. 카탈리나는 JSP 컨테이너이기도 하기 때문에, 요청받은 서블릿이 JSP 서블릿인지도 반드시 확인해야 함 83 | - JSP 페이지라면 JSP 페이지에 해당하는 실제 클래스를 취득하려는 시도를 함 84 | - JSP 페이지에 해당하는 서블릿의 이름을 찾지 못하면 servletClass 변수의 값이 사용됨 85 | - servletClass 변수의 값이 설정되어있지 않다면 예외를 발생시킴 86 | 4. 서블릿 클래스의 이름이 파악되었다면, 로더를 얻음 87 | - 로더가 없다면 예외를 발생시킴 88 | - 카탈리나가 제공하는 `org.apache.catalina` 패키지에 속하는 특별한 서블릿일 경우, classLoader 변수에는 또 다른 ClassLoader 인스턴스가 할당됨으로써 카탈리나의 내부에 접근 가능하게 됨 89 | 5. 서블릿을 초기화하기 전에, isServletAllowed 메소드를 호출하여 이 서블릿을 로드하는 것이 허락되어 있는지 확인 90 | - 보안 확인 통과: 로드하려는 서블릿이 ContainerServlet인지 확인. ContainerServlet일 경우 카탈리나의 내부에 접근할 수 있음 91 | - 서블릿이 ContainerServlet일 경우, ContainerServlet의 setWrapper 메소드에 StandardWrapper 인스턴스를 전달하여 호출함 92 | - 서블릿이 STM 서블릿이라면 이 서블릿 인스턴스는 인스턴스 풀에 추가됨 93 | 6. BEFORE_INIT_EVENT를 발생시키고 서블릿의 init 메소드 호출 94 | - `javax.servlet.ServletConfig` 객체를 참조하는 facade 변수가 init 메소드에 전달됨 95 | 7. loadOnStartup 변수에 정수 값이 할당되어 있고 서블릿이 JSP 페이지라면, 서블릿의 service 메소드를 추가로 호출 96 | 8. AFTER_INIT_EVENT 이벤트 발생시킴 97 | 9. 마지막으로 서블릿 인스턴스 반환 98 | 99 | 100 | ### ServletConfig 객체 101 | StandardWrapper 클래스는 Wrapper 인터페이스뿐만 아니라 `javax.servlet.ServletConfig` 인터페이스도 구현한다. 102 | 103 | StandardWrapper 클래스는 서블릿의 init 메소드에 자기 자신을 그대로 전달하지 않는다. 대신 자신을 StandardWrapper**Facade** 인스턴스에 래핑해, **서블릿 프로그래머가 대부분의 메소드를 호출할 수 없게 한다**. 104 | 105 | #### ServletConfig 인터페이스의 메소드 106 | ##### getServletContext 107 | StandardWrapper 인터페이스는 반드시 StandardContext의 하위 컨테이너여야 한다(그렇다고 StandardWrapper가 StandardContext를 확장하는 하위 클래스인 것은 아니다). 108 | 109 | ServletContext 객체는 StandardContext 객체의 getServletContext 메소드를 호출함으로써 얻을 수 있다. 110 | 111 | ##### getServletName 112 | 서블릿의 이름을 반환한다. 메소드는 단순히 ContainerBase 클래스의 getName 메소드를 호출하고 있는데, ContainerBase 클래스는 StandardWrapper 클래스의 상위 클래스이다. 113 | 114 | ContainerBase 클래스의 setName 메소드를 호출하여 name의 값을 변경할 수 있다. 115 | 116 | ##### getInitParameter 117 | 지정한 초기화 파라미터의 값을 반환한다. 118 | 119 | StandardWrapper에서는 초기화 파라미터를 HashMap 타입의 parameters 변수에 저장한다. addInitParameter 메소드에 파라미터의 이름과 값을 전달해 호출함으로써 parameters의 내용을 추가할 수 있고, findInitParameter 메소드는 파라미터의 이름을 받아 parameters의 get 메소드를 호출한다. 120 | 121 | ##### getInitParameterNames 122 | 초기화 파라미터들의 모든 이름을 포함하는 Enumeration을 반환한다. 123 | 124 | Enumerator 클래스는 `java.util.Enumeration`을 구현하는 클래스로서 `org.apache.catalina.util` 패키지에 포함되어 있다. 125 | 126 | 127 | ### 부모 자식 관계 128 | - 래퍼는 개별 서블릿에 해당하는 컨테이너 129 | - 래퍼는 그 하위의 컨테이너, 즉 자식 컨테이너를 가질 수 없으며 addChild 메소드는 호출되어서는 안됨 130 | - 반드시 Context의 구현 클래스만이 래퍼의 부모 컨테이너가 될 수 있음 131 | 132 | 133 | ## StandardWrapperFacade 134 | StandardWrapper 인스턴스는 자신이 로드한 서블릿의 init 메소드를 호출한다. init 메소드는 `javax.servlet.ServletConfig`를 필요로 하며, 클래스 스스로가 ServletConfig 인터페이스를 구현하고 있으므로 객체 자신을 init 메소드에 전달할 수 있다. 135 | 136 | 하지만 보안 상 문제로 StandardWrapper 클래스는 자신을 StandardWrapperFacade 인스턴스로 래핑한다. 둘 다 `javax.servlet.ServletConfig` 인터페이스를 구현하고 있다. 137 | 138 | StandardWrapper 객체가 서블릿의 init 메소드를 호출할 때에는 StandardWrapperFacade의 인스턴스를 전달한다. 139 | 140 | getServletContext 메소드 호출 시, StandardWrapper 클래스의 getServletContext 메소드를 호출하지만 ServletContext 객체 자체가 아닌 퍼사드를 반환한다. 141 | 142 | 143 | ## StandardWrapperValve 144 | StandardWrapperValve 클래스는 StandardWrapper 인스턴스의 기본 밸브이다. 145 | 146 | ### 역할 147 | - 서블릿과 연관된 모든 필터(filter) 실행 148 | - 서블릿의 service 메소드 호출 149 | 150 | ### invoke 메소드의 구현 내용 151 | - StandardWrapper의 allocate 메소드를 호출해 StandardWrapper가 나타내는 서블릿의 인스턴스를 얻음 152 | - **createFilterChain** 메소드를 호출해 필터 체인(filter chain) 생성 153 | - ApplicationFilterChain의 인스턴스를 생성하고, 여기에 StandardWrapper로 표현되는 서블릿에 적용해야 할 모든 필터를 추가한다. 154 | - 필터 체인의 **doFilter** 메소드 호출, 서블릿의 service 메소드 호출 155 | - 필터 체인 반환(release) 156 | - 래퍼의 deallocate 메소드 호출 157 | - 서블릿이 더 이상 필요하지 않다면, 래퍼의 unload 메소드 호출 158 | 159 | ### 필터(filter)란? 160 | 서블릿의 앞단에 위치해, 들어오는 요청과 나가는 응답에 대해 변형을 가할 수 있는 컴포넌트. 서블릿 2.3부터 새로 추가됐다. 161 | 162 | 163 | ## FilterDef 164 | `org.apache.catalina.deploy.FilterDef` 클래스는 전개 설명자(web.xml)의 filter 요소에 정의되어 있는 필터를 나타낸다. 165 | 166 | FilterDef 클래스가 갖는 각 특성들은 전개 설명자에서 filter 요소 내의 하위 요소들을 나타낸다. 또한, 이 클래스는 이 필터를 위한 초기화 파라미터들을 포함하는 Map 타입의 parameters라는 변수를 가진다. 추가 시 초기화 파라미터의 이름/값 쌍을 추가한다. 167 | 168 | 169 | ## ApplicationFilterConfig 170 | `org.apache.catalina.core.ApplicationFilterConfig` 클래스는 `javax.servlet.FilterConfig` 인터페이스를 구현한다. 이 클래스는 웹 애플리케이션 시작시에 생성된 필터 인스턴스들을 관리한다. 171 | 172 | 이 클래스의 생성자 메소드에 **`org.apache.catalina.Context`** 객체와 **FilterDef** 객체를 전달하며 객체를 생성시킬 수 있다. 173 | 174 | - Context 객체: 웹 애플리케이션을 나타냄 175 | - FilterDef 객체: 필터의 정의를 나타냄 176 | 177 | 178 | ## ApplicationFilterChain 179 | `org.apache.catalina.core.ApplicationFilterChain` 클래스는 `javax.servlet.FilterChain` 인터페이스를 구현한다. StandardWrapperValve 클래스의 invoke 메소드는 ApplicationFilterChain 인스턴스를 생성하고 doFilter 메소드를 호출한다. 180 | 181 | ApplicationFilterChain 클래스의 doFilter 메소드는 이 체인의 존재하는 첫 필터의 doFilter 메소드를 호출한다. 체인에 존재하는 필터를 전부 호출시켰다면, 이후에는 서블릿의 service 메소드가 호출되도록 진행된다. 필터가 chain.doFilter를 호출하지 않는다면 그 다음 필터는 호출될 수 없다. 182 | 183 | 184 | ## 요약 185 | 카탈리나에서 Wrapper 인터페이스를 구현하는 표준 구현 클래스인 StandardWrapper 클래스와 필터와 필터 체인에 대해 알아보았다. 186 | 187 | ![11장 요약 이미지1](./img/11-1.jpg) 188 | 189 | ![11장 요약 이미지2](./img/11-2.jpg) 190 | 191 | ### 참고 문헌 192 | - 톰캣 최종분석 193 | -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/img/11-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/YangDonghwa/markdown/img/11-1.jpg -------------------------------------------------------------------------------- /src/YangDonghwa/markdown/img/11-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/YangDonghwa/markdown/img/11-2.jpg -------------------------------------------------------------------------------- /src/YuJaeGuk/Chap01/README.md: -------------------------------------------------------------------------------- 1 | # 1장 2 | 3 | ## 간단한 웹 서버 4 | 5 | 자바 웹 서버가 어떻게 동작하는지 설명하는 장. 6 | 7 | 웹 서버는 클라이언트(브라우저)와 통신할 때 HTTP를 사용하기 때문에 HTTP 서버라고도 함. 8 | 9 | 자바기반 웹 서버는 java.net.Socket 과 java.net.ServerSocket 2개의 클래스를 사용하며, HTTP 통신을 통해서 수행. 10 | 11 | 따라서 1장에서는 먼저 HTTP와 2개의 클래스를 다룸. 12 | 13 |
14 | 15 | 16 | ### HTTP 17 | 18 | Hypertext Transfer Protocol 19 | 20 | 인터넷에서 웹 서버와 클라이언트(브라우저)가 데이터를 주고받는 데 필요한 프로토콜. 21 | 22 | Request / Response 방식의 프로토콜으로 클라이언트가 파일을 요청하면, 웹 서버는 그 요청에 대해서만 응답하는 구조. 23 | 24 | 신뢰성 높은 TCP 연결을 사용하며, TCP 80 포트를 기본으로 사용. 25 | 26 | HTTP에서는 항상 클라이언트가 연결을 맺고 HTTP 요청을 송신함으로써 트랜잭션이 시작. 27 | 28 | 웹 서버는 스스로 클라이언트에 연결을 맺지 않으며, 클라이언트로의 응답 연결을 시도하지도 않음. 29 | 30 |
31 | 32 | #### HTTP Request 33 | 34 | ##### HTTP Request의 3가지 요소 35 | 36 | ###### - Method URI(Uniform Resource Identifier) Protocol/Version 37 | 38 | ###### - Request headers 39 | 40 | ###### - Entity body 41 | 42 |
43 | 44 | ##### HTTP Request Message Example 45 | 46 | ~~~ 47 | POST /examples/default.jsp HTTP/1.1 48 | Accept: text/plain; text/html 49 | Accept-Language: en-gb 50 | Connection: Keep-Alive 51 | Host: localhost 52 | User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows98) 53 | Content-Length: 33 54 | Content-Type: application/x-www-form-urlencoded 55 | Accept-Encoding: gzip, deflate 56 | 57 | lastName=Franks&firstName=Michael 58 | ~~~ 59 |
60 | 61 | ##### Request의 첫째 줄에는 METHOD-URI-PROTOCOL이 표시. 62 | 63 | ###### POST /examples/default.jsp HTTP/1.1 64 | 65 | ##### POST는 Method를 나타내며, /examples/default.jsp는 URI를, HTTP/1.1은 Protocol/Version을 나타냄. 66 | 67 | ##### 각각의 HTTP Request는 HTTP 표준에 명시되어있는 다양한 요청메소드 가운데 하나를 사용할 수 있음. 68 | 69 |
70 | 71 | W3의 HTTP 1.1 Method Define 72 | 73 | [https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html] 74 | 75 |
76 | 77 | ##### URI는 인터넷 자원을 지정. 78 | 79 | ##### 대부분의 경우, URI는 서버의 최상위 디렉토리의 상대경로로 나타냄으로 URI는 항상 슬래시 '/'로 시작. 80 | 81 | ##### Protocol/Version은 사용하고 있는 HTTP Protocol의 Version을 나타냄. 82 | 83 |
84 | 85 | ##### Request Headers에는 클라이언트 환경에 관한 유용한 정보를 포함. 86 | 87 | ##### 예를 들어 브라우저에 설정되어있는 언어, 문서 본체의 길이 등. 각 개별의 헤더 정보는 캐리지 리턴과 개행문자(CRLF)로 구분됨. 88 | 89 |
90 | 91 | ##### Request headers와 Entity body사이에는 빈 줄이 하나 존재하는데, 이는 HTTP Request 서식에 있어서 본체가 어디서부터 시작하는지 알려주는 역할. 92 | 93 | ##### 방금 살펴본 HTTP Request Example에서는 다음과 같은 간단한 본체가 있음. 94 | 95 | ###### lastName=Franks&firstName=Michael 96 | 97 | ###### ( 대개의 경우 HTTP Request의 본체는 이보다 훨씬 길음 ) 98 | 99 |
100 | 101 | #### HTTP Response 102 | 103 | ##### HTTP Request와 마찬가지로 HTTP Response 역시 다음 3가지 요소로 구성. 104 | 105 | ###### Protocol Status-Code Description 106 | 107 | ###### Response headers 108 | 109 | ###### Entity body 110 | 111 |
112 | 113 | ##### HTTP Response Message Example 114 | ~~~ 115 | HTTP/1.1 200 OK 116 | Server: Microsoft-IIS/4.0 117 | Date: Mon, 5 Jan 2004 13:13:33 GMT 118 | Content-Type: text/html 119 | Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT 120 | Content-Length: 112 121 | 122 | 123 | 124 | HTTP Response Example 125 | 126 | 127 | Welcome to Brainy Software 128 | 129 | 130 | ~~~ 131 | 132 |
133 | 134 | ##### Response headers의 첫 줄은 Request headers와 비슷. 135 | 136 | ##### Protocol은 HTTP Version 1.1을 사용. Request는 문제 없이 처리(200=성공 코드). 모든 사항이 이상 없음(OK). 137 | 138 |
139 | 140 | ##### Response headers는 Resquest headers와 마찬가지로 클라이언트에게 유용한 정보를 포함. 141 | 142 |
143 | 144 | ##### Response headers와 Entity Body는 빈 줄로 구분되어있으며, Entity body는 HTML 컨텐츠 145 | 146 |
147 | 148 | ### Socket Class 149 | 150 | ##### 소켓은 네트워크 연결에 있어서 End point에 해당. 151 | 152 | ##### 애플리케이션은 소켓을 사용해 네트워크에 데이터를 읽거나 씀. 153 | 154 | ##### 서로 다른 2개의 컴퓨터에 있는 소프트웨어 애플리케이션은, 네트워크 연결을 통해 Byte Stream을 전송하고 수신하며 서로간에 통신. 155 | 156 | ##### 한 애플리케이션에서 다른 애플리케이션으로 메시지를 전달하려면 상대방 컴퓨터의 IP와 상대방 애플리케이션의 소켓이 사용하는 포트 번호를 알아야 함. 157 | 158 | ##### 자바에서 소켓은 java.net.Socket 클래스에 해당. 159 | 160 | Oracle Java의 Socket Class 161 | 162 | [https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html] 163 | 164 |
165 | 166 | ### ServerSocket Class 167 | 168 | ##### Socket 클래스는 어디에서든 원격 서버 애플리케이션에 연결할 수 있는 '클라이언트' 소켓을 나타냄. 169 | 170 | ##### 그러나 HTTP 서버, FTP 서버 애플리케이션을 구현할 때는 다른 방식으로 접근해야함. 171 | 172 | ##### 서버 애플리케이션은 특성상 클라이언트가 언제 접속을 시도할지 알 수 없는 상태에서 항상 대기하고 있어야 하기 때문. 173 | 174 | ##### 서버 애플리케이션이 항상 접속을 대기하게 하려면 java.net.ServerSocket 클래스를 사용함. 175 | 176 | Oracle Java의 ServerSocket Class 177 | 178 | [https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html] 179 | 180 |
181 | 182 |
183 | 184 | ### 참고 문헌 185 | 186 | [https://ko.wikipedia.org/wiki/HTTP] 187 | 188 | [https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html] 189 | 190 | [https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html] 191 | 192 | [https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html] 193 | -------------------------------------------------------------------------------- /src/YuJaeGuk/Chap02/README.md: -------------------------------------------------------------------------------- 1 | # 2장 2 | 3 | ## 간단한 서블릿 컨테이너 4 | 5 | ### 서블릿 컨테이너 6 | 7 | #### 서블릿을 관리하며 네크워크 통신, 서블릿의 생명주기 관리, 스레드 기반의 병렬처리를 대행 8 | 9 | #### 웹 클라이언트로 부터 HTTP Request가 전달되면 해당 HTTP Request를 해석. 10 | 11 | #### 서블릿의 service 메서드를 ServletRequet, ServletResponse 매개변수와 함께 호출. 12 | 13 | #### 서블릿을 실행하고 관리하는 역할을 하며 개발자가 신경써야 할 복잡한 부분을 대신 처리해줌. 14 | 15 | #### 16 | 17 | 18 | 을 하는데 개발자가 신경써야 할 복잡한 부분을 대신 처리하여 주기 때문에 편리하다. 한가지 예로 자바애플리케이션 개발시 필요한 main()메소드가 존재하지 않는 것이 이때문이다. 다음은 서블릿 컨테이너의 주된 역할이다. 19 | - 생명 주기 관리 : 서블릿을 로드해 초기화(init 메소드) 한다. 또 클라이언트의 요청으로 서블릿 메소드를 호출하며, 서블릿 컨테이너가 종료되면 서블릿을 종료시키고(destroy 메소드 호출) 메모리를 정리한다. 20 | - 통신 지원 : 웹서버로부터 받은 요청을 분석해 서블릿을 실행시키고 서블릿에서는 웹서버의 정보를 확인할 수 있도록 하는 기능을 제공한다. 21 | - 멀티스레딩 지원 : 클라이언트의 요청에 따라 서블릿을 생성하고, 이미 생성된 서블릿에 대한 요청은 스레드를 생성해 실행한다. 22 | 23 | 서블릿 컨테이너를 개발하는 방법을 설명하는 장. 24 | 25 | 첫번째 애플리케이션은 서블릿 컨테이너가 어떻게 동작하는지 쉽게 이해할 수 있도록 최대한 간단히 설계. 26 | 27 | 그리고 이 애플리케이션을 약간은 복잡한 두번째 서블릿 컨테이너로 발전시킬 것. 28 | 29 |
30 | 31 | 서블릿 컨테이너는 간단한 서블릿 뿐만 아니라 정적 자원도 처리함. 32 | 33 | 34 |
35 | 36 | 37 | ### HTTP 38 | 39 | Hypertext Transfer Protocol 40 | 41 | 인터넷에서 웹 서버와 클라이언트(브라우저)가 데이터를 주고받는 데 필요한 프로토콜. 42 | 43 | Request / Response 방식의 프로토콜으로 클라이언트가 파일을 요청하면, 웹 서버는 그 요청에 대해서만 응답하는 구조. 44 | 45 | 신뢰성 높은 TCP 연결을 사용하며, TCP 80 포트를 기본으로 사용. 46 | 47 | HTTP에서는 항상 클라이언트가 연결을 맺고 HTTP 요청을 송신함으로써 트랜잭션이 시작. 48 | 49 | 웹 서버는 스스로 클라이언트에 연결을 맺지 않으며, 클라이언트로의 응답 연결을 시도하지도 않음. 50 | 51 |
52 | 53 | #### HTTP Request 54 | 55 | ##### HTTP Request의 3가지 요소 56 | 57 | ###### - Method URI(Uniform Resource Identifier) Protocol/Version 58 | 59 | ###### - Request headers 60 | 61 | ###### - Entity body 62 | 63 |
64 | 65 | ##### HTTP Request Message Example 66 | 67 | ~~~ 68 | POST /examples/default.jsp HTTP/1.1 69 | Accept: text/plain; text/html 70 | Accept-Language: en-gb 71 | Connection: Keep-Alive 72 | Host: localhost 73 | User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows98) 74 | Content-Length: 33 75 | Content-Type: application/x-www-form-urlencoded 76 | Accept-Encoding: gzip, deflate 77 | 78 | lastName=Franks&firstName=Michael 79 | ~~~ 80 |
81 | 82 | ##### Request의 첫째 줄에는 METHOD-URI-PROTOCOL이 표시. 83 | 84 | ###### POST /examples/default.jsp HTTP/1.1 85 | 86 | ##### POST는 Method를 나타내며, /examples/default.jsp는 URI를, HTTP/1.1은 Protocol/Version을 나타냄. 87 | 88 | ##### 각각의 HTTP Request는 HTTP 표준에 명시되어있는 다양한 요청메소드 가운데 하나를 사용할 수 있음. 89 | 90 |
91 | 92 | W3의 HTTP 1.1 Method Define 93 | 94 | [https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html] 95 | 96 |
97 | 98 | ##### URI는 인터넷 자원을 지정. 99 | 100 | ##### 대부분의 경우, URI는 서버의 최상위 디렉토리의 상대경로로 나타냄으로 URI는 항상 슬래시 '/'로 시작. 101 | 102 | ##### Protocol/Version은 사용하고 있는 HTTP Protocol의 Version을 나타냄. 103 | 104 |
105 | 106 | ##### Request Headers에는 클라이언트 환경에 관한 유용한 정보를 포함. 107 | 108 | ##### 예를 들어 브라우저에 설정되어있는 언어, 문서 본체의 길이 등. 각 개별의 헤더 정보는 캐리지 리턴과 개행문자(CRLF)로 구분됨. 109 | 110 |
111 | 112 | ##### Request headers와 Entity body사이에는 빈 줄이 하나 존재하는데, 이는 HTTP Request 서식에 있어서 본체가 어디서부터 시작하는지 알려주는 역할. 113 | 114 | ##### 방금 살펴본 HTTP Request Example에서는 다음과 같은 간단한 본체가 있음. 115 | 116 | ###### lastName=Franks&firstName=Michael 117 | 118 | ###### ( 대개의 경우 HTTP Request의 본체는 이보다 훨씬 길음 ) 119 | 120 |
121 | 122 | #### HTTP Response 123 | 124 | ##### HTTP Request와 마찬가지로 HTTP Response 역시 다음 3가지 요소로 구성. 125 | 126 | ###### Protocol Status-Code Description 127 | 128 | ###### Response headers 129 | 130 | ###### Entity body 131 | 132 |
133 | 134 | ##### HTTP Response Message Example 135 | ~~~ 136 | HTTP/1.1 200 OK 137 | Server: Microsoft-IIS/4.0 138 | Date: Mon, 5 Jan 2004 13:13:33 GMT 139 | Content-Type: text/html 140 | Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT 141 | Content-Length: 112 142 | 143 | 144 | 145 | HTTP Response Example 146 | 147 | 148 | Welcome to Brainy Software 149 | 150 | 151 | ~~~ 152 | 153 |
154 | 155 | ##### Response headers의 첫 줄은 Request headers와 비슷. 156 | 157 | ##### Protocol은 HTTP Version 1.1을 사용. Request는 문제 없이 처리(200=성공 코드). 모든 사항이 이상 없음(OK). 158 | 159 |
160 | 161 | ##### Response headers는 Resquest headers와 마찬가지로 클라이언트에게 유용한 정보를 포함. 162 | 163 |
164 | 165 | ##### Response headers와 Entity Body는 빈 줄로 구분되어있으며, Entity body는 HTML 컨텐츠 166 | 167 |
168 | 169 | ### Socket Class 170 | 171 | #### 소켓은 네트워크 연결에 있어서 End point에 해당. 172 | 173 | #### 애플리케이션은 소켓을 사용해 네트워크에 데이터를 읽거나 씀. 174 | 175 | #### 서로 다른 2개의 컴퓨터에 있는 소프트웨어 애플리케이션은, 네트워크 연결을 통해 Byte Stream을 전송하고 수신하며 서로간에 통신. 176 | 177 | #### 한 애플리케이션에서 다른 애플리케이션으로 메시지를 전달하려면 상대방 컴퓨터의 IP와 상대방 애플리케이션의 소켓이 사용하는 포트 번호를 알아야 함. 178 | 179 | #### 자바에서 소켓은 java.net.Socket 클래스에 해당. 180 | 181 | Oracle Java의 Socket Class 182 | 183 | [https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html] 184 | 185 |
186 | 187 | ### ServerSocket Class 188 | 189 | #### Socket 클래스는 어디에서든 원격 서버 애플리케이션에 연결할 수 있는 '클라이언트' 소켓을 나타냄. 190 | 191 | #### 그러나 HTTP 서버, FTP 서버 애플리케이션을 구현할 때는 다른 방식으로 접근해야함. 192 | 193 | #### 서버 애플리케이션은 특성상 클라이언트가 언제 접속을 시도할지 알 수 없는 상태에서 항상 대기하고 있어야 하기 때문. 194 | 195 | #### 서버 애플리케이션이 항상 접속을 대기하게 하려면 java.net.ServerSocket 클래스를 사용함. 196 | 197 | Oracle Java의 ServerSocket Class 198 | 199 | [https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html] 200 | 201 |
202 | 203 |
204 | 205 | ### 참고 문헌 206 | 207 | [https://ko.wikipedia.org/wiki/HTTP] 208 | [https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html] 209 | [https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html] 210 | [https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html] 211 | -------------------------------------------------------------------------------- /src/YuJaeGuk/Chap03/README.md: -------------------------------------------------------------------------------- 1 | Hi -------------------------------------------------------------------------------- /src/YuJaeGuk/Chap04/README.md: -------------------------------------------------------------------------------- 1 | # 4장 2 | 3 | ## 배경 4 | 5 | ### 웹 서버인 아파치와 WAS인 톰캣 6 | 7 | ~~~ 8 | 1. 톰캣은 정적인 페이지를 로드하기에 아파치보다 느린 처리 속도를 보인다. 9 | 2. 톰캣은 설정할 수 있는 내용들이 아파치에 비해 부족하다. 10 | 3. 톰캣은아파치보다 부하에 약하다. 11 | 4. 톰캣만으로는 서블릿/JSP밖에 서비스할 수 없다. 12 | 5. 톰캣과 아파치를 연동하여 부하분담이 가능하다. (다수의 톰캣 구성으로의 로드밸러싱의 의미가 아닌 톰캣만으로 처리가능한 static page를 apache에서 대신함으로써의 부하를 분담) 13 | ~~~ 14 | 15 | ##### 그러나 1번 5번 이유는 톰캣 5.5 이상에서 httpd의 native 모듈을 사용해서 static page를 처리하는 기능을 제공함으로 이 경우 httpd와 톰캣이 같은 모듈을 사용하는 셈이니 성능에서 차이가 날 이유가 없어짐 16 | 17 | ### 그럼에도 불구하고 아파치 + 톰캣을 사용하는 이유는? 18 | 19 | ~~~ 20 | 1. 80 포트의 사용권한 21 | - 리눅스에서 ~1024 포트까지는 root 계정만 사용이 가능하므로, tomcat을 80 포트로 운영하기에는 문제. 22 | (기본적으로 root 계정이 아니면 톰켓을 80 포트로 서버를 열 수 없음.) 23 | (열더라도 접속이 되지 않으며, 억지로 하면 방화벽을 이용하는 방법이 있긴하다.) 24 | (리눅스에서는 1024 이하의 포트는 root 이외의 계정에게는 권한을 주지 않음.) 25 | (보안이 문제인데 톰켓을 root 권한으로 80포트로 돌릴때 만약 tomcat이 뚫리면 해커는 리눅스 서버를 root 권한으로 서버를 해킹) 26 | (톰캣은 서버 접속자에게 같은 권한을 준다.) 27 | 2. 정적 데이터 처리 28 | - 톰캣의 성능이 아무리 좋아졌다고 해도, image/css 등과 같은 정적 데이터는 httpd에서 처리하는게 더 믿음직 하다. 29 | 또한 압축등과 같은 정적 데이터를 처리하는 다양한 옵션등이 존재한다.(물론 압축 전송 옵션은 톰캣7에도 있다) 30 | 3. 로드 밸런싱 31 | - 동일 서버내에서 한 서비스를 여러 tomcat에서 서비스하고자 할때 사용한다. apache를 연동하는 가장 큰 목적. 32 | ~~~ 33 | 34 | #### 커넥터는 아파치와 톰캣 중간에 위치해 있으며, 아파치로 들어온 URL이 JSP 또는 DO 호출 방식이면 톰캣 커넥터로 연결되어 톰캣으로 연결되며, URL이 HTML 혹은 정적파일이라면 아파치에서 처리. 35 | 36 |
37 | 38 | ## 커넥터 39 | 40 | 카탈리나의 주요 모듈중 하나로 요청을 컨테이너에 연결해주는 모듈. 41 | 42 | 커넥터는 전달 받은 각 HTTP Request에 대해 Resquest Object와 Response Object를 구성하고 컨테이너에 전달. 43 | 44 | 이후 컨테이너는 커넥터로부터 Request, Response Object를 전달받고, 서블릿의 Service Method를 호출하는 구조. 45 | 46 |
47 | 48 | ## 톰캣의 기본 커넥터 49 | 50 | 여기서 Connector는 톰캣 혼자서(아파치-와 같은 웹서버 없이) 구동되어서 하나의 웹서버로서 활용 되기 위한 용도의 Connector 51 | 52 | 서블릿 컨테이너에 플러그인이 될 수 있는 독립적인 모듈. 53 | 54 | 코요테, mod_jk, mod_jk2, mod_webapp와 같은 다양한 커넥터가 존재함 55 | 56 | 기본적으로 톰캣 커넥터는 다음과 같은 요구 조건을 충족해야함 57 | 58 | - org.apache.catalina.Connector 인터페이스 구현 59 | 60 | - org.apache.catalina.Request 인터페이스를 구현하는 Request Object 생성 61 | 62 | - org.apache.catalina.Response 인터페이스 구현하는 Response Object 생성 63 | 64 |
65 | 66 | 톰캣의 기본 커넥터도 커넥터와 비슷하게 작동. 67 | 68 | 들어오는 HTTP Request를 기다리고, Request, Response를 생성, 컨테이너로 Request, Response Object를 전달. 69 | 70 | 커넥터는 org.apache.catalina.Container 인터페이스의 invoke Method를 통해 Request/Response Object를 컨테이너로 전달. 71 | 72 | 이후 컨테이너는 invoke Method 내에서 Servlet Class Load, service Method Call, Session Management, Error Message Logging 등 다양한 일을 함. 73 | 74 |
75 | 76 | 4장의 기본 커넥터는 3장의 커넥터에 없는 몇 가지 최적화 기법이 적용됨. 77 | 78 | 첫째로 다양한 객체를 위한 풀을 제공하여 과도한 객체 생성을 방지. 79 | 80 | 둘째로 코드 내의 다양한 곳에서 문자열 대신 문자의 배열을 사용함으로 애플리케이션 성능을 향상시킴. 81 | 82 | ~~~ 83 | 1. String은 자바에서 불변하므로 암호가 일반 텍스트로 저장되는 경우 가비지 컬렉터가 암호를 지울 때까지 메모리에서 사용될 수 있으며, 문자열이 재사용성을 위해 String Pool에서 사용되므로 남이있을 가능성이 크고, 장기간 동안 메모리에 저장되어 보안 위협이 됨. 84 | 85 | 2. 메모리 덤프에 액세스 권한이 있는 사용자는 일반 텍스트로 암호를 찾을 수 있게 됨. 이가, 일반 텍스트보다 암호화 된 암호를 사용하는 또 다른 이유. 따라서 Char Array에 암호를 저장하면 암호를 도용하는 보안 위험에서 완화 됨. 86 | 87 | 3. Char Array를 사용하면 명시적으로 데이터를 지우거나, 덮을 쓸 수 있어 시스템의 어느 위치에도 암호가 나타나지 않음. 일반 문자열을 사용하게되면, 비밀번호를 로그에 프린트할 확률이 훨씬 높아짐. 88 | ~~~ 89 | 90 | 또 한 가지 주목할 것은, HTTP 0.9와 HTTP 1.0 뿐만 아니라 HTTP 1.1의 새로운 기능까지도 모두 지원하게 되므로, HTTP 커넥터의 내부 작동 원리를 이해하기 위해 HTTP 1.1의 새로운 세 가지 특징을 알아보아야 함. 91 | 92 |
93 | 94 | ## HTTP 1.1의 새로운 특징 95 | 96 | ### 지속 연결성 97 | 98 | HTTP 1.1 이전에는 웹 서버가 요청받은 자원을 브라우저에게 전송하면 즉시 연결을 닫게 했었음. 그러나 브라우저가 페이지를 요청할 때, 페이지와 그 밖의 자원에 대해 각각 연결해서 다운로드 되어야 한다면 이 과정은 전체적으로 느려질 수 밖에 없음. 이것이 HTTP 1.1에서 지속 연결을 지원하는 이유. 99 | 100 | 지속 연결은 HTTP 1.1의 기본 연결 방법으로, connection Request Header에 keep-alive 값을 지정함으로 명시적으로 지속 연결을 사용 101 | ~~~ 102 | connection: keep-alive 103 | ~~~ 104 | 105 | ### 청크 인코딩 106 | 107 | 지속 연결성으로 서버는 여러 자원에서 바이트 스트림을 전송할 수 있게 됐고, 클라이언트는 동일한 연결을 다중 요청할 수 있게 됐음. 108 | 109 | 따라서 데이터를 전송하는 측에서는 수신하는 측이 올바르게 바이트를 해석할 수 있게 요청이나 응답의 컨텐츠 길이를 헤더에 포함해야하나, 전송하는 측에서 바이트를 얼마나 전송할지 미리 알 수 없는 경우가 많다. 110 | 111 | ~~~ 112 | HTTP 1.0 에서는 서버는 단지 content-length 헤더를 무시하고 데이터를 계속 쓰기만 하며, 클라이언트는 파일의 끝에 도달했다는 의미인 '-1' 값을 받을 때까지 계속 읽었음 113 | ~~~ 114 | 115 | HTTP 1.1에서는 transfer-encoding이라는 특별헨 헤더를 통해 바이트 스트림이 chunk 단위로 전송될 것임을 가리키며, chunk에서는 실제 데이터가 전송되기 전에 16진수로 된 길이 값과 캐리지 리턴/개행 문자가 먼저 전송되고 chunk의 길이가 0이면 하나의 트랜잭션이 종료. 116 | 117 | ~~~ 118 | 38바이트의 데이터를 2개의 chunk로 전송하는 예 119 | 1D\r\n --> 16진수로 첫번째자리 1 = 16, 두번째자리 D = 13 120 | I'm as helpless as a kitten u --> 총 29바이트 121 | 9\r\n --> 9를 나타냄 122 | p a tree. --> 총 9바이트 123 | 0\r\n --> 트랜잭션 종료 124 | ~~~ 125 | 126 | ### 100 상태 코드의 사용 127 | 128 | HTTP 1.1 클라이언트에서는 서버에게 데이터 수신 거절 여부를 알아보고자 Request Body를 전송하는 낭비를 없애기 위해, Expect: 100-continue 헤더를 전송해 Request Body를 전송하기에 앞서 서버측의 승인을 기다림. 이 방법은 클라이언트가 전송하고자 하는 요청 본체의 양이 많을 경우, 서버가 이를 받아들일 수 있는지 먼저 확인하고자 할 때 흔히 사용함. 129 | 130 | ~~~ 131 | 클라이언트 -> Expect: 100-continue 데이터를 보내도 될까요? 132 | 서버 -> HTTP/1.1 100 Continue 네, 보내세요 133 | ~~~ 134 | 135 |
136 | 137 | ## Connector 인터페이스 138 | 커넥터는 반드시 org.apache.catalina.Connector 인터페이스를 구현해야 함. 139 | 140 | 주로 컨테이너를 연결할 때 사용하는 setContainer, 커넥터와 연결되어있는 컨테이너를 리턴하는 getContainer, HTTP Request에 대한 Request Object를 생성하는 createRequest, Response Object를 생성하는 createResponse가 있다. 141 | -------------------------------------------------------------------------------- /src/YuJaeGuk/Chap10/LoginConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soobinnn/How-Tomcat-Works-Study/fad23eee781c02f4693e6bcfb705a3f452331d46/src/YuJaeGuk/Chap10/LoginConfig.png -------------------------------------------------------------------------------- /src/YuJaeGuk/Chap10/README.md: -------------------------------------------------------------------------------- 1 | # 10장 2 | 3 | ## 보안 4 | 5 | 특정 컨텐츠에 대한 접근을 제한하기 위한 웹 애플리케이션의 보안에 대해 설명. 6 | 7 | 10장은 보안과 관련해 보안 주체(pincipals), 보안 역할(roles), 로그인 설정(login config), 인증자(authenticators) 등에 대한 개념을 다룸. 8 | 9 |
10 | 11 | 서블릿 기술은 web.xml 파일의 설정을 통해 원하는 컨텐츠에 보안 제약을 적용할 수 있도록 지원. 12 | 13 | 서블릿 컨테이너는 인증자(authenticator)라는 밸브를 통해 보안 제약 기능을 지원. 14 | 15 | 서블릿 컨테이너가 구동될 때 인증자 밸브는 컨텍스트의 파이프라인에 추가되고, 래퍼 밸브보다 이전에 호출되어. 16 | 17 | 사용자가 올바른 사용자 이름과 암호를 입력했다면 인증자 밸브는 요청을 처리할 다음 밸브를 호출하고, 인증에 실패하면 인증자 밸브는 다음 밸브를 호출하지 않고 리턴되어 사용자는 요청한 서블릿을 볼 수 없음. 18 | 19 | 이 과정에서 인증자 밸브는 유효한 사용자 이름과 암호의 집합을 가지고 있는 영역(Realm) 컴포넌트에 존재하는 authenticate 메소드에 사용자 이름과 암호를 전달하여 호출. 20 | 21 |
22 | 23 | ### Realm (영역) 24 | 25 | Realm은 사용자를 인증하는 데 필요한 컴포넌트. 26 | 27 | 보통 하나의 컨텍스트에 연결되며, 하나의 컨테이너는 하나의 영역만을 가질 수 있음. 28 | 29 | 영역은 사용자의 이름과 암호를 지닐 수 있으며, 또는 사용자 이름과 암호가 저장된 저장소에 접근할 수 있음. 30 | 31 | ~~~ 32 | 보통 톰캣에서는 tomcat-users.xml 파일에 저장하지만, 관계형 데이터베이스 같은 다른 자원을 이용해 인증할 수 있는 영역을 구현할 수 있다. 33 | ~~~ 34 | 35 | 카탈리나에서는 "org.apache.catalina.Realm" 인터페이스로 대변되며, 이 인터페이스의 가장 중요한 4개의 메소드는 주체를 리턴받는 authenticate 메소드를 가지고 있음. 36 | 37 | ~~~ 38 | public principal authenticate(String username, String credentials); // -- 일반적으로 사용되는 메소드 39 | public principal authenticate(String username, byte[] credentials); 40 | public principal authenticate(String username, String digest, String nonce, 41 | String nc, String cnonce, String qop, String realm, String md5a2); 42 | public principal authenticate(X509Certificate certs[]); 43 | ~~~ 44 | 45 |
46 | 47 | ### GenericPrincipal (주체) 48 | 49 | 주체란 인증을 받는 실체를 말하며, 사용자나 그룹 등 인증을 받는 어떠한 대상을 가리키며, "java.security.principal" 인터페이스로 나타며, 생성자 메소드에서 볼 수 있듯이 영역과 항상 연결되어있어야 함. 50 | 51 | ~~~ 52 | // 카탈리나에서 principal의 구현 클래스는 org.apache.catalina.realm.GenericPrincipal 클래스 53 | public GenericPrincipal(Realm realm, String name, String password){ 54 | . 55 | . 56 | 57 | public GenericPrincipal(Realm realm, String name, String password, List roles){ 58 | . 59 | . 60 | ~~~ 61 | 62 | 영역과 반드시 이름과 암호를 갖고있어야 하며, 추가로 역할의 목록을 전달할 수 있고, hasRole 메소드를 통하여 이 주체가 지정된 역할을 가지고 있는지 여부를 판단할 수 있음. 63 | ~~~ 64 | public boolean hasRole(String role){ 65 | if (role==null) 66 | return false; 67 | return (Arrays.binarySearch(roles, role) >= 0); 68 | ~~~ 69 | 70 |
71 | 72 | ### loginConfig (로그인 설정) 73 | 74 | ![](https://github.com/Soobinnn/How-Tomcat-Works-Study/blob/master/src/YuJaeGuk/Chap10/LoginConfig.png) 75 | 76 | 톰캣은 시작시 web.xml 파일을 읽어들이며, web.xml 파일에 login-config 요소를 포함하고 있으며 그 특성을 설정함. 77 | 78 | ~~~ 79 | web.xml 80 | 81 | 82 | BASIC 83 | 84 | 85 | 86 | name 87 | 88 | . 89 | . 90 | ~~~ 91 | 92 | 인증방식(auth-method)은 BASIC이며, 컨테이너에 지정한 내용(tomcat-user.xml)과 맵핑하는 정보(security-role)를 기술. 93 | 94 |
95 | 96 | ### Authenticator (인증자) 97 | 98 | org.apache.catalina.Authenticator 인터페이스는 인증자를 대변하며, 이 인터페이스는 아무런 메소드를 가지고 있지 않은 마커 인터페이스로 다른 컴포넌트가 어떤 컴포넌트에 대해 instance of를 수행해 그 컴포넌트가 인증자인지 여부를 확인하는 역할을 함. 99 | 100 |
101 | 102 | 마커 인터페이스 참고 - https://sumin172.tistory.com/136 103 | --------------------------------------------------------------------------------