내용 목차
본 장에서는 JEUS 웹 컨테이너 내의 Java EE Web 모듈(Web Application, Context)을 조립, 등록, 모니터링하는 모든 방법에 대하여 설명한다.
본 절에서는 Context에 관련된 기본 개념과 디렉터리 구조, 관련 파일에 대해서 설명한다.
다음 그림은 Context/Web application에 관련된 웹 컨테이너의 그림이다.
웹 애플리케이션은 클라이언트의 요청에 의한 웹 기반의 서비스(예를 들면, 장바구니에 물품을 추가하거나, 장바구니 안의 물품을 구매하거나 웹 기반의 경매 사이트에서 물건을 사기 위해 브라우징하는 등)를 수행하기 위한 static과 dynamic content의 집합이라고 할 수 있다.
Static Content
Static Content는 사전에 제작되어서, 더 이상 어떤 처리도 하지 않고 단순히 반환되는 모든 종류의 데이터들을 의미한다. 다음은 그 예이다.
HTML 페이지
단순 텍스트 파일
이미지 파일(GIF, JPEG)
비디오(MPEG)
기타
Dynamic Content
변하지 않는 정적과는 반대로 Dynamic Content는 클라이언트 요청에 대한 응답으로 생성한 모든 종류의 데이터를 말한다. 이러한 Dynamic Content는 클라이언트와 상호 연동하기 위해 또는 사용자의 선택과 선호에 따라 응답 페이지를 생성할 때 사용한다.
JEUS 웹 컨테이너를 설명할 때 Java EE 웹 애플리케이션에 초점이 맞춰진다. Static 부분 뿐만 아니라 이런 애플리케이션의 패키징과 개발 규칙들까지 모두 Java EE 5 스펙, Servlet 2.5스펙, JSP 2.1 스펙에 정의되고 기술되어 있다.
Context는 논리적으로 Context Group 또는 가상 호스트 하위에 존재한다. 가상 호스트는 Context Group의 서브 컴포넌트이다. 등록된 Context는 묵시적 기본 가상 호스트로 작동한다. 본 장에서는 간략하게 기본 가상 호스트의 일부로 Context Group 아래에 Context를 설정한다고 가정한다.
가상 호스트의 더 자세한 사항은 “제7장 가상 호스트”를 참고한다.
Java EE 웹 애플리케이션은 Java EE 웹 컨테이너에 배포되고 등록되기 전에 특수한 Archive(라이브러리) 파일에 패키징하여 포함시켜야 한다. 이 애플리케이션들을 패키지할 때 사용하는 Archive들은 JAR/ZIP 파일들로 '.war' 확장자를 가지고 있다. 이들은 JAR 유틸리티를 사용하여 다른 JAR Archive를 생성하는 방법과 동일하게 생성된다.
WAR 파일의 구조는 다음과 같다.
이 디렉터리는 선택 사항이고, 존재하면 JAR 포맷에 정의된 Descriptor 파일인 "MANIFEST.MF" 파일을 포함한다.
이 디렉터리는 필수 사항이고, 웹 애플리케이션의 Servlet, Java 유틸리티 클래스들, JAR/ZIP 라이브러리들이 포함되어 있다. 이 디렉터리 바로 하위에는 다음과 같은 컴포넌트들이 존재한다.
파일 | 설명 |
---|---|
web.xml | Java EE 표준 웹 애플리케이션 DD 파일로 웹 애플리케이션의 모든 메타 정보를 가지고 있다. 그리고, 클라이언트에 의해 접근 가능한 모든 Servlet과 JSP들의 목록을 가지고 있어야 한다. 그렇지 않으면 웹 컨테이너는 파일들의 위치를 알 수 없다. |
jeus-web-dd.xml | JEUS의 웹 애플리케이션 DD로 자세한 설명은 “6.3.2. Deployment Descriptor(DD) 파일 설정”을 참고한다. |
classes\ | Servlet 클래스들과 유틸리티 클래스들이 하위 디렉터리에 포함되어 있다. 표준 Java 패키지 구조로 정리되어 있다. |
lib\ | 웹 애플리케이션에 필요한 Java 라이브러리들을 포함한다. 라이브러리들은 JAR 또는 ZIP 파일들로 패키지되어 이 경로에 저장된다. 이 파일들의 내용들은 자동적으로 모든 Servlet의 클래스 경로에 추가된다. |
tlds\ | JSP 페이지들을 위한 custom tag library descriptor들을 포함한다. |
HTML, JSP, 이미지 파일들과 같은 content들을 포함하고 있는 임의의 이름의 디렉터리이다.
본 절에서는 Java EE Web Context(웹 애플리케이션)가 JEUS 웹 컨테이너에 어떻게 등록되는지에 대해 설명한다.
여기에서 “등록”은 “디플로이”와 “설치”라는 의미와 동일하다. 등록은 앞에서 간단히 설명한 바와 같이, 모든 Java EE 웹 애플리케이션을 JEUS 웹 컨테이너에서 요청을 받고 수행할 수 있도록 준비하는 과정을 의미한다.
모든 Java EE 호환 WAR 파일은 JEUS 웹 컨테이너에 등록(즉, 설치 또는 디플로이라고도 한다)될 수 있다. 그러므로 몇 개의 WAR/context는 Context Group 안에 그룹화된다(“제3장 Context Group” 참조).
WAR 파일이 정상적으로 등록되면 Context(WAR 파일을 가진)는 클라이언트 요청을 서비스할 준비가 되었다는 것이다.
WAR 파일 등록에는 다음과 같은 하위 작업들이 포함된다.
임의의 위치에 새로운 Context 디렉터리를 생성한 다음 WAR 파일의 내용을 풀어놓는다.
새롭게 생성된 Context의 디렉터리 구조는 WAR 파일의 구조와 동일하다. 사용상의 편의를 위해서 JEUS_HOME\webhome\app_home\<contextName>으로 디렉터리를 생성할 것을 권장한다.
새로운 웹 애플리케이션 DD파일을 작성한다.
이 파일은 jeus-web-dd.xml로 명명되며, WEB-INF 디렉터리에 web.xml과 같이 위치시킨다.
web.xml에서 발견할 수 있는 특정 레퍼런스(EJB 레퍼런스 또는 보안 역할)를 실제 시스템 자원에 매핑한다.
예를 들어, 이것은 Symbolic EJB 레퍼런스(web.xml에서 발견할 수 있는)를 실제의 EJB JNDI 이름에 프로그래머가 정의한 보안 역할을 실제 시스템의 사용자로 매핑하는 것을 포함한다. 이러한 모든 매핑 정보는 jeus-web-dd.xml 파일에 입력된다.
다른 웹 컨테이너 특정 설정을 새로운 Context에 맞춰 설정한다.
이런 설정의 예에는 사용자 로그 작동 방식 등이 있다(“jeus-web-dd.xml”에도 설정됨).
“6.3.9. Context 등록”을 참조하여 웹 애플리케이션을 등록한다.
선택적으로 처음 JSP를 호출할 때 보다 빠르게 실행하기 위해서 미리 컴파일한 JSP 파일을 사용할 수도 있다.
웹 애플리케이션이 디플로이할 때 JEUSMain.xml의 <application> 태그로 등록되었다면 웹 컨테이너를 재시작한다.
이 모든 작업들은 편집기를 이용하여 할 수도 있지만 WebAdmin을 이용할 것을 권장한다.
우선 디플로이할 웹 애플리케이션을 위해 jeus-web-dd.xml의 <context-path>에 설정할 다음과 같은 작업 디렉터리를 생성한다. 여기서는 <context-path>가 ‘/MyContext’라고 가정한다.
JEUS_HOME\webhome\app_home\MyContext
Context 디렉터리는 임의의 위치에 생성하여도 무방하다. 그 임의의 위치에서 관련 파일 작업을 한 후에 exploded 또는 archive 형태로 JEUSMain.xml의 <application> 등록, archive 형태로 autodeploy에 위치시켜서 재기동하거나 직접 콘솔 툴(jeusadmin)에서 디플로이가 가능하다.
webhome\app_home을 Context 디렉터리의 루트 디렉터리로 권장하는 이유는 콘솔 툴(jeusadmin)이나 웹 관리자(WebAdmin)의 사용에 있어서 편리함을 제공하기 때문이다.
예를 들어 콘솔 툴(jeusadmin)에서 디플로이 이후 인자로 Context 이름만 입력하여도 app_home의 웹 애플리케이션을 찾아서 디플로이해 주며, WebAdmin에서는 app_home의 웹 애플리케이션의 목록을 자동으로 출력한다.
Context 디렉터리를 생성한 후에는 웹 애플리케이션 디렉터리 구조 [그림 6.3]을 따라서 관련 파일을 생성하여 위치시킨다.
Context 디렉터리 생성이 완료되면 그 context를 위해 JEUS 전용의 DD를 생성한다. Well-formed DD를 작성할 때에는 JEUS_HOME\lib\schemas\jeus\jeus-web-dd.xsd을 준수하여 작성한다.
생성할 DD 파일의 위치는 다음과 같다. 여기에는 web.xml이 같이 위치한다.
JEUS_HOME\webhome\app_home\MyContext\WEB-INF
Web DD 파일이 생성되면 내용을 작성한다. 다음에서 이 파일에 포함될 설정 요소들에 대해서 설명한다. 좀 더 자세한 정보는 JEUS_HOME\docs\reference\schema\index.html에서 jeus-web-dd.xml를 참조한다.
다음은 JEUS Web Context 설정 파일의 예로 몇몇 항목들은 생략되어 있다.
[예 6.1] Web Context 설정 파일 : <<jeus-web-dd.xml>>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="6.0"> <context-path>/examples</context-path> <enable-jsp>true</enable-jsp> <auto-reload> <enable-reload>true</enable-reload> <check-on-demand>true</check-on-demand> <reload-timeout>30000</reload-timeout> </auto-reload> <max-instance-pool-size>20</max-instance-pool-size> <added-classpath> <class-path>c:\mylib\lib.jar</class-path> </added-classpath> <allow-indexing> <directory>/images/</directory> </allow-indexing> <deny-download> <file>/data/secret.dat</file> <extension>dat</extension> <directory>/data/</directory> </deny-download> <aliasing> <alias> <alias-name>/images/</alias-name> <real-path>c:\web\images\</real-path> </alias> </aliasing> <file-caching> <max-idle-time>1800</max-idle-time> <max-cache-memory>10</max-cache-memory> <directory>/images/</directory> </file-caching> . . . <jsp-engine> . . . </jsp-engine> <session-config> . . . </session-config> <fast-deploy>true</fast-deploy> <library-ref> <library-name>myLibrary</library-name> <specification-version>2.0</specification-version> </library-ref> <properties> <property> <key>jeus.servlet.jsp.print.null.as.emptystring</key> <value>true</value> </property> </properties> </jeus-web-dd>
다음은 설정 태그에 대한 설명이다.
태그 | 설명 |
---|---|
<context-path> | context를 접근하기 위해 사용되는 기본URL이다. 이것은 반드시 “/”로 시작해야 하고, 하나의 가상 호스트나 Context Group 내에서 유일한 것이어야 한다. 예를 들면 http://www.foo.com/examples/index.html과 같은 URL의 context 경로는 “/examples”가 된다. |
<user-log> | context group의 user log 설정과 같다. “3.3.4. Logging 설정”을 참고한다. |
<auto-reload> | 디스크의 Servlet 클래스가 변경되었을 때 자동적으로 로딩이 되는지에 대한 설정이다. 이는 Container의 클래스 리로딩을 모니터링 하는 thread에 설정된 시간 주기에 따라 정기적으로 확인된다. 이 하위 태그에는 클래스 리로딩이 새로운 요청이 들어왔을 때만 발생하도록 하는 것도 있다. 클래스를 리로딩 할 때 time-out으로 실패가 날 경우 그 대기시간을 조절할 수 있다. |
<enable-jsp> | false로 설정했을 때, JSP 기능을 완전히 사용하지 않는다. 기본은 JSP를 지원한다. |
<max-instance-pool-size> | SingleThreadedModel을 implement한 Servlet은 한 번에 하나의 thread에 의해서만 수행된다. 이러한 Servlet을 여러 개 동시에 수행하려면 SingleThreadedModel의 Pool이 생성되어야 한다. 이 설정은 재사용 가능한 Pool의 최대 크기를 설정한다(Pool 크기 이상 생성은 가능). 참고로 SingleThreadModel은 deprecated된 스펙이므로 사용을 권장하지 않는다. |
<added-classpath> | Servlet들이 컴파일되고 실행될 때 접근 가능하게 해야할 Java 클래스 디렉터리 또는 JAR 파일들의 리스트들이다. 기본적으로 “classes\” 디렉터리의 클래스 파일들과 ”WEB-INF\” 디렉터리의 “lib\” 디렉터리의 JAR 파일들은 접근 가능한 위치들이다. |
<allow-indexing> | 클라이언트가 요청했을 때, 실제 디렉터리 목록이 출력되는 URL 경로의 목록이다. 디렉터리의 목록이 출력되는 경우는 클라이언트에서 요청한 파일이 디렉터리에 없을 때이다. 대신에 디렉터리 목록의 파일을 하나 선택하면, 그 파일의 내용이 출력된다. |
<deny-download> | 어떤 경우에라도 직접적으로 다운로드 받거나 접근할 수 없는 자원들을 규정하기 위한 필터이다. 이 필터들은 파일 이름의 확장자로, 파일 이름과 함께 경로로 지정할 수 있다. 필터의 파일 이름과 디렉터리들은 context document base 디렉터리의 상대적인 경로로 주어진다. |
<aliasing> | Custom URL 경로에 대한 디렉터리 매핑을 의미한다. 다른 위치의 디렉터리를 URL 요청 경로에 매핑을 시킬 필요가 있는 경우에 aliasing 기능을 사용한다. |
<file-caching> | Container의 응답 속도 향상을 위해 런타임 메모리에 어떤 Static Content(HTML, GIF, JPEG등)가 Caching이 되어야 할지 결정한다. 이 항목의 하위 설정들은 Cache 메모리의 최대 크기(단위: MB), 요청되지 않을 때 Cache 내에 머무를 수 있는 Static Content의 최대 크기, Caching 되어야 할 콘텐츠가 있는 경로가 있다. |
<jsp-engine> | 현재 컨텍스트의 JSP 페이지에 대한 설정이다. 이 element는 WEBMain.xml의 JSP Engine 설정과 동일하다. 만약에 jeus-web-dd.xml 파일에 JSP Engine 설정이 되어 있다면, 여기에 설정된 내용이 WEBMain.xml에 설정된 해당 컨텍스트의 JSP Engine 설정보다 우선한다. 이 element에 대한 설정 정보는 “3.3.3. JSP 엔진 설정”을 참고한다. |
<session-config> | Context 레벨의 Session을 설정한다. 현 Context에 적용되는 Session과 관련하여 여러 가지 설정을 할 수 있다. Context 레벨에서 정의된 Session 설정은 Web Container 레벨, Context Group 레벨에서 정의된 설정을 반복한다. 다만 Context 레벨에서 설정한 설정은 웹 컨테이너, Context Group에서 설정한 것보다 높은 우선 순위를 가지고 적용된다. Session 설정의 자세한 부분은 “2.3.7. Session”을 참고한다. |
<webinf-first> | 클래스 로딩 우선 순위에 관한 옵션으로 true로 설정되면 이 애플리케이션에 한해서는 다른 설정과 관계없이 WEB-INF 하위의 클래스를 우선적으로 로딩하게 된다. |
<attach-stacktrace-on-error> | 서버에 문제가 발생하였을 때 오류의 상세 내역을 브라우저로 보여줄 것인지에 대한 설정이다. 이 메시지는 개발하는 경우에는 유용하지만 운영하는 경우에는 제거하는 것이 좋다. ContextGroup에서도 설정이 가능하지만 전체적으로 설정하지 않고 특정 Context만 설정할 때는 여기에서 설정한다. |
<encoding> | request, response에 대한 인코딩 설정이다. 이 설정은 ContextGroup의 설정과 동일하며 동시에 설정될 경우 여기의 설정이 우선 적용된다. 자세한 내용은 “3.3. Context Group 설정”을 참조한다.
|
<web-security> | sendRedirect할 경우에 주소값 검증에 대한 정책 설정 등 웹 애플리케이션에 국한된 보안 정책들의 설정 그룹이다. |
Shared Library는 애플리케이션별로 필요한 라이브러리를 WEB-INF\lib가 아닌 JEUS_HOME\lib\shared 아래에 추가한 후에 여러 애플리케이션에서 공유하여 사용할 수 있도록 해준다. JEUS 재기동없이도 libraries.xml 파일 수정 후 모듈을 다시 디플로이하면 변경된 라이브러리가 반영된다.
Shared Library를 추가하기 위해서는 다음과 같이 JEUS_HOME\lib\shared\libraries.xml 파일에서 JSF, JSTL 라이브러리는 변경하지 않고 <library> 태그를 추가하고 공유할 JAR 파일들을 지정한다.
버전은 스펙 버전과 구현체 버전을 각각 지정할 수 있다. 다음은 스펙 버전에 대한 예이고, 구현체 버전에 대한 설명은 "JSF1.2, JSTL1.2 라이브러리 설정"을 참고한다.
[예 6.2] Shared Library 레퍼런스 추가 : <<libraries.xml>>
<libraries xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
<!-- DO NOT MODIFY JSF1.2 and JSTL1.2 libraries!!! -->
<library>
<library-name>jsf</library-name>
<specification-version>1.2</specification-version>
<implementation-version>1.2_06</implementation-version>
<files dir=".">
<include name="jsf-injection-provider.jar"/>
</files>
<files dir="jsf_ri_1.2_06"/>
</library>
<library>
<library-name>jstl</library-name>
<specification-version>1.2</specification-version>
<implementation-version>1.2</implementation-version>
<files dir="jstl_1.2"/>
</library>
<!-- Add user libraries from here -->
<library>
<library-name>myLibrary</library-name>
<specification-version>2.0</specification-version>
<implementation-version>2.1</implementation-version>
<files dir=".">
<include name="commons-logging.jar"/>
<include name="commons-util.jar"/>
</files>
<files dir="myLib-2.1"/>
</library>
</libraries>
Shared Library를 참조하기 위해서는 jeus-web-dd.xml에 다음과 같이 <library-ref>를 설정하면 된다. 버전이 명시되지 않은 경우는 libraries.xml에 등록된 동일한 이름의 라이브러리 중에서 최상위 버전을 참조한다.
<library-ref> <library-name>myLibrary</library-name> <specification-version>2.0</specification-version> </library-ref>
[예 6.2]에 설명한 libraries.xml 파일에는 JSF1.2와 JSTL1.2가 Shared Library로 이미 추가되어 있음을 확인할 수 있다. Java EE 5 스펙은 JSF1.2와 JSTL1.2를 포함하고 있기 때문에 WEB-INF\lib 아래에 JSF, JSTL 라이브러리 없이도 정상적으로 디플로이가 되어야 한다.
JEUS_HOME\lib\system 아래에 JSF와 JSTL 구현체를 두면 Java EE 5 스펙을 만족할 수 있지만 이 경우에는 하나의 JSF, JSTL 구현체만 사용할 수 있는 문제가 발생한다. JSF 구현체는 1.2 뿐만 아니라 1.1 버전도 있고 SUN RI가 아닌 Apache의 myfaces도 있기 때문에 JEUS에서는 여러 가지 버전의 구현체를 사용할 수 있도록 Shared Library 형태로 JSF, JSTL을 제공한다.
구현체의 위치, 종류에 따라 사용되는 구현체의 경우는 다음과 같이 나누어 진다.
WEB-INF\lib 아래에 JSF나 JSTL 구현체가 있을 경우
이들 구현체가 우선 사용된다.
WEB-INF\lib 아래에 JSF나 JSTL 구현체가 없을 경우
lib\shared 아래의 JSF RI 1.2, JSTL 1.2가 사용된다. JSF와 JSTL은 Java EE 5에 포함되어 있기 때문에 예외적인 경우로 jeus-web-dd.xml에 <library-ref> 설정 없이도 사용할 수 있다.
WEB-INF\lib 아래에 myfaces1.1 구현체가 있을 경우
myfaces가 우선 사용된다.
WEB-INF\lib 아래에 JSF 구현체가 없다면
lib\shared 아래의 JSF RI 1.2가 사용된다. JSF 구현체 간에는 호환성 문제가 있기 때문에 개발하는 경우와 다른 JSF 구현체가 사용된다면 문제가 발생할 수 있다.
특정 버전의 JSF나 JSTL을 사용하기 위해서는 libraries.xml에 library를 등록하고 jeus-web-dd.xml에서 <library-ref>로 참조하도록 할 수도 있다. 가장 간단한 방법은 기존 J2EE1.4의 웹 모듈처럼 WEB-INF\lib 아래에 JSF, JSTL 구현체를 위치시키면 된다.
JSF1.2의 경우는 Managed Bean에서 @EJB나 @Resource 같은 annotation으로 WAS가 제공하는 리소스에 접근할 수 있고 WAS는 annotation으로 명시된 리소스들을 injection할 수 있어야 한다.
JEUS에서는 JSF1.2 RI에 JEUS Injection을 지원하기 위해서 Shared Library로 jsf-injection-provider.jar를 제공하고 있다.
JEUS 5에서는 사용자의 편의성과 Servlet 2.3 이전에 개발된 애플리케이션을 위해서 표준이 아닌 기능도 제공하고 JSP Parsing에 문법 체크도 최신 스펙에 비해서 엄격하지 않게 적용하였다.
Servlet 2.3 이전에는 Servlet과 JSP를 직접 사용해서 웹 애플리케이션을 개발했지만 현재는 웹 프레임워크를 점차 사용하는 추세이고 Java EE 5에서는 표준 웹 프레임워크로 JSF1.2와 JSTL1.2가 추가되었다.
웹 프레임워크는 Servlet, JSP 스펙에 대한 참조 구현체인 Tomcat을 사용해서 주로 개발하고 테스트하기 때문에 스펙과 다른 비표준 기능 지원시 다양한 호환성 문제가 발생할 수 있다. 그래서 JEUS 6에서는 JEUS 5에 대한 하위 호환성을 위해서 기존의 JSP Parser도 지원하지만 디폴트는 Jasper 기반의 JSP Parser로 변경하였다.
JEUS 5에서 문제가 없었던 웹 모듈도 JEUS 6에 디플로이하면 여러 가지 에러가 발생할 수 있다. JEUS 6에서 Servlet 2.5, JSP 2.1, JSF 1.2, JSTL 1.2의 최신 스펙을 사용하기 위해서는 JSP 컴파일할 때 발생하는 에러 메시지와 JSP 라인을 확인 후 수정해서 업그레이드해야 한다.
디플로이할 때 다음과 같은 형태로 에러가 발생한다면 '/jsp/customtag12/date.jsp'의 2번째 라인의 62번째 컬럼에서 어떤 문제가 있는지 확인해야 한다.
[2007.01.15 10:02:29][2][b029] [mycontainer-12] [WEB-5697] [examples#examples] par
sing 35 jsp file(s)
[2007.01.15 10:02:33][1][b029] [mycontainer-12] fail to parse jsp file : /jsp/cust
omtag12/date.jsp
<<__Exception__>>
jeus.servlet.jsp2.Jsp2EngineException:
file:C:/jeus6/webhome/johan_mycontainer/examples/examples_war___/jsp/customtag12/d
ate.jsp(2,62)
Failed to load or instantiate TagExtraInfo class: customtag12.ShowDateTei
at jeus.servlet.jsp2.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler
.java:59)
at jeus.servlet.jsp2.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:46
7)
at jeus.servlet.jsp2.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:29
8)
만약 최신 스펙이 필요하지 않고 기존에 개발된 모듈을 그대로 JEUS6에서 사용하기 위해서는 다음과 같이 jeus-web-dd.xml에 JEUS5 호환의 JSP Parser가 이 Context에만 적용되도록 설정할 수 있다.
<properties> <property> <key>jeus.servlet.jsp.modern</key> <value>false</value> </property> </properties>
jeus-web-dd.xml에 설정한 옵션은 이 Context에만 적용되고 WEBMain.xml에서는 ContextGroup이나 VirtualHost 단위로도 옵션을 설정할 수 있다. Container 레벨에서 옵션을 적용하기 위해서는 다음과 같은 VM 옵션을 설정할 수 있다.
-Djeus.servlet.jsp.modern=false
VM 옵션은 ContextGroup, VirtualHost, Context에서 Override되기 때문에 jeus-web-dd.xml에 설정한 옵션이 최종적으로 적용된다. jeus-web-dd.xml에 옵션 설정이 없다면 상위의 디폴트 설정이 적용된다. 나머지 옵션들은 “JEUS Reference Book”의 “1.7. 서블릿/웹 시스템 프로퍼티”에서 확인할 수 있다.
추가 옵션은 하위 호환성이 필요한 경우만 적용하고 신규 개발은 추가적인 옵션 없이 표준에 따라 개발할 것을 권장한다.
Servlet 표준에서는 Servlet 또는 JSP가 아닌 경우 WAS에서 제공하는 기본 서블릿(Default Servlet)으로 처리하도록 되어 있다. 이를 ResourceServlet이라고 한다. JEUS v6.0 Fix#9부터는 이러한 ResourceServlet을 직접 매핑해서 사용할 수 있다.
다음과 같이 web.xml에서 <servlet-mapping> 설정에 'jeus.servlet.servlets.ResourceServlet'로 매핑한다.
<servlet-mapping> <servlet-name>jeus.servlet.servlets.ResourceServlet</servlet-name> <url-pattern>/static/*</url-pattern> </servlet-mapping>
jeus-web-dd.xml을 통해 애플리케이션별로 적용할 웹 보안 설정에 대해 설명한다.
애플리케이션은 javax.servlet.http.HttpServletResponse.sendRedirect(String location) 표준 API를 통해서 "302 Found" 응답을 보낼 수 있다. 이때 기본적으로 Location으로 넘겨주는 문자열에 대해서 아무런 확인을 하지 않고 그대로 URL로 전환해서 Location 헤더로 설정한다. 그렇기 때문에 악의적인 사용자가 CRLF injection과 같은 공격을 할 수 있다. 이를 방지하기 위해 애플리케이션은 jeus.servlet.security.RedirectStrategy 인터페이스를 구현해서 jeus-web-dd.xml에 설정할 수 있다.
다음은 jeus.servlet.security.RedirectStrategy 인터페이스를 설정한 예이다.
[예 6.3] Redirect Location 보안 설정 인터페이스: <<RedirectStrategy>>
package jeus.servlet.security; import javax.servlet.http.HttpServletRequest; public interface RedirectStrategy { /** * Makes the redirect URL. * * @param location the target URL to redirect to, for example "/login" */ String makeRedirectURL(HttpServletRequest request, String location) throws IllegalArgumentException; }
설정한 인터페이스를 다음과 같이 구현할 수 있다.
[예 6.4] Redirect Location 보안 설정 구현 예: <<RejectCrlfRedirectStrategy>>
package jeus.servlet.security; import javax.servlet.http.HttpServletRequest; public class RejectCrlfRedirectStrategy implements RedirectStrategy { private Pattern CR_OR_LF = Pattern.compile("\\r|\\n"); @Override String String makeRedirectURL(HttpServletRequest request, String location) throws IllegalArgumentException { if (CR_OR_LF.matcher(location).find()) { throw new IllegalArgumentException("invalid characters (CR/LF) in redirect location"); } return makeAbsolute(location); } private String makeAbsolute(String location) { // make code for make absolute path } }
jeus-web-dd.xml의 설정 방법은 다음과 같다.
[예 6.5] Redirect Location 보안 설정 : <<jeus-web-dd.xml>>
... <web-security> <redirect-strategy-ref> jeus.servlet.security.RejectCrlfRedirectStrategy </redirect-strategy-ref> </web-security> ..
<redirect-strategy-ref>는 jeus.servlet.security.RedirectStrategy 인터페이스를 구현한 클래스 이름이다. 이 클래스는 웹 애플리케이션의 클래스 패스에 포함시키면 된다.
위에서 제시한 jeus.servlet.security.RejectCrlfRedirectStrategy의 경우 기본적으로 제공하는 RedirectStrategy이며 CR, LF 또는 CRLF가 있는 Location이 sendRedirect API로 넘어올 경우 500 에러가 발생한다.
그 외에도 CR, LF 또는 CRLF 문자열을 빈 문자열로 치환해주는 jeus.servlet.security.RemoveCrlfRedirectStrategy를 제공한다.
본 절에서는 web.xml에 정의된 보안 Role을 실제 시스템 사용자와 사용자 그룹에 어떻게 지정하는지 살펴보겠다. 이 매핑은 “jeus-web-dd.xml”에 기술된다
개발자가 다음의 내용을 DD(web.xml)에 정의하였다고 가정하자.
[예 6.6] 보안 Role 매핑 : <<web.xml>>
<web-app> <security-role> <role-name>manager</role-name> </security-role> <security-role> <role-name>developer</role-name> </security-role> <servlet> . . . </servlet> <security-constraint> <web-resource-collection> <web-resource-name> MyResource </web-resource-name> <url-pattern>/jsp/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>manager</role-name> </auth-constraint> </security-constraint> </web-app>
위의 예제에서는 굵은 글씨로 두 개의 보안 Role이 선언되어 있다. 세 번째 굵은 글자 라인은
manager role이 <security-constraint> 태그에 어떻게 사용되어 지는지를 보여주고
있다.
웹 애플리케이션이 실제의 시스템에 디플로이될 때, manager와 developer Role을 시스템의 특정한 사용자로 바인딩해야 한다. 이 매핑은 <context><role-mapping>에 정의되어 있다. <role-mapping> 태그는 0개 이상의 <role-permission> 태그들을 가지고 있다. 이 태그를 이용하여 <principal-to-role> 매핑을 정의한다. 다음의 하위 태그들과 함께 바인딩된다.
태그 | 설명 |
---|---|
<role>(1개, 필수) | web.xml에 정의된 role name이며, 위의 예에서는 role name이 “manager”이다. |
<principal>(0개 이상) | role name과 연계되어야 할 JEUS가 관리하는 사용자의 이름이다. 더 자세한 사항은 “JEUS Security 안내서”의 “3.3. 웹 모듈 보안 설정”을 참고한다. |
2개의 Role “manager”와 “developer”가 “Peter”와 “Linda”로 각각 매핑되어 있다.
[예 6.7] 보안 Role 매핑 : <<jeus-web-dd.xml>>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="6.0">
. . .
<role-mapping>
<role-permission>
<principal>Peter</principal>
<role>manager</role>
</role-permission>
<role-permission>
<principal>Linda</principal>
<role>developer</role>
</role-permission>
</role-mapping>
. . .
</jeus-web-dd>
Role이 실제 사용자에 매핑되듯이 EJB 레퍼런스, 자원 레퍼런스, 관리되는 객체의 레퍼런스를 실제 시스템 자원에 매핑할 필요가 있다.
다음은 Symbolc 레퍼런스 매핑의 예이다.
[예 6.8] Symbolc 레퍼런스 매핑 : <<web.xml>>
<web-app> . . . <ejb-ref> <ejb-ref-name>ejb/account</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <home>com.mycompany.AccountHome</home> <remote>com.mycompany.Account</remote> </ejb-ref> <resource-ref> <res-ref-name>jdbc/EmployeeAppDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> <resource-env-ref> <resource-env-ref-name> jms/StockQueue </resource-env-ref-name> <resource-env-ref-type> javax.jms.Queue </resource-env-ref-type> </resource-env-ref> . . . </web-app>
이 웹 애플리케이션을 등록하기 위하여, web.xml에 있는 <ejb-ref>, <resource-ref>, <resource-env-ref> 아래의 모든 Symbolc 레퍼런스 이름들이 실제 의도하는 자원의 JNDI 이름과 매핑되어야 한다. 이 매핑은 jeus-web-dd.xml에서 <ejb-ref>, <res-ref>, <res-env-ref>를 추가하면 완성된다.
이 태그들은 다음과 같은 하위 태그들을 가지는 <jndi-info> 태그가 있다.
<jndi-info>
태그 | 설명 |
---|---|
<ref-name> | reference name(ref name)은 web.xml과 Servlet 코드에 정의된 것과 같은 것이다. |
<export-name> | JNDI export name은 레퍼런스 이름이 뜻하는 실제의 자원 export-name이다. |
다음은 JNDI 이름이 위의 3개의 레퍼런스와 매핑된 jeus-web-dd.xml의 예이다.
[예 6.9] Symbolc 레퍼런스 매핑 : <<jeus-web-dd.xml>>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="6.0"> . . . <ejb-ref> <jndi-info> <ref-name>ejb/account</ref-name> <export-name>AccountEJB</export-name> </jndi-info> </ejb-ref> <res-ref> <jndi-info> <ref-name>jdbc/EmployeeAppDB</ref-name> <export-name>EmployeeDB</export-name> </jndi-info> </res-ref> <res-env-ref> <jndi-info> <ref-name>jms/StockQueue</ref-name> <export-name>StockQueue</export-name> </jndi-info> </res-env-ref> . . . </jeus-web-dd>
웹 애플리케이션을 디플로이하는 방법은 다음과 같은 3가지 방법이 있다.
콘솔 툴(jeusadmin) 이용
웹 애플리케이션을 디플로이하는 자세한 내용은 "JEUS Server 안내서"를 참고한다.
WebAdmin 이용
웹 애플리케이션을 디플로이하는 자세한 내용은 "JEUS WebAdmin 안내서"를 참고한다.
JEUSMain.xml의 <application> 등록
본 절에서 예제를 사용해서 설명한다. Context를 등록한 후 컨테이너를 재기동해야 한다.
다음은 JEUSMain.xml의 <application> 등록하는 예제이다.
JEUS_HOME은 c:\jeus6로 가정하고 “Examples”와 “MyApp”라는 2개의 Context를 등록한다. 두 Context의 요청 URL은 “/examples”와 “/myapp”라고 지정되어 있다. 예를 들어, http://www.foo.com/examples/index.html를 요청하면 “Examples” context의 “index.html”을 반환한다(웹 컨테이너의 context group의 HTTP 리스너의 포트가 80으로 설정되어 있다고 가정한다).
[예 6.10] Context 등록 : <<JEUSMain.xml>>
<jeus-system xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <node> <name>tmax1</name> ... </node> <application> <name>Examples</name> <path> c:\jeus6\webhome\app_home\examples </path> <deployment-target> <target> <engine-container-name> tmax1_container1 </engine-container-name> <web-context-group> <name>MyGroup</name> <virtual-host-name> www.foo.com </virtual-host-name> </web-context-group> </target> </deployment-target> <deployment-type>COMPONENT</deployment-type> <web-component/> </application> <application> <name>MyApp</name> <path> c:\jeus6\webhome\app_home\myapp </path> <deployment-target> <target> <engine-container-name> tmax1_container1 </engine-container-name> <web-context-group> <name>MyGroup</name> <virtual-host-name> www.foo.com </virtual-host-name> </web-context-group> </target> </deployment-target> <deployment-type>COMPONENT</deployment-type> <web-component/> </application> </jeus-system>
등록의 마지막 과정으로 웹 애플리케이션의 JSP 페이지들을 프리컴파일할 수 있다. 이것은 JEUS_HOME\bin\의 JSP 배치 컴파일러 appcompiler, jspc 툴로 한다.
appcompiler는 JEUS와 관계없이 오프라인 상태에서 JSP 프리컴파일이 가능하고, jspc는 JEUS가 기동되고 디플로이가 완료된 모듈에 대해서 프리컴파일을 수행할 수 있다. 이 툴들에 대해서는 “JEUS Reference Book”의 “4.4. appcompiler”와 “JEUS Reference Book”의 “4.7. jspc”의 설명을 참고한다.
JSP 배치 컴파일러를 이용하면, JSP가 처음 요청되었을 때의 성능을 향상시킬 수 있다. 배치 컴파일러는 새로운 웹 애플리케이션이 등록되었을 때 각 JSP를 수동으로 요청해야 하는 번거로움을 없애주므로 시스템 관리자에게는 편리성을 제공한다.
위의 과정을 거친 후 등록이 정확이 되었는지 수동으로 확인하기 위해서 jeusadmin 툴을 사용하여 웹 컨테이너를 재시작한다.
다음의 예에서 JEUS 노드 이름은 “johan”이라고 가정한다.
웹 컨테이너를 재시작하기 위해서는 해당 container 를 정지하고 다시 시작해야 한다. 해당 Container 이름이 "johan_container1"이라고 가정한다. 사용자 이름과 암호를 입력한다.
johan> downcon johan_container1
웹 컨테이너가 시작하고 운영 환경에 새롭게 등록한 context가 반영되는 것을 확인 할 수 있다.
johan> startcon johan_container1
jeusadmin을 시작한다.
C:\jeusadmin johan_container1
사용자 이름과 암호를 입력한다.
‘info’ 명령을 수행한다.
johan> info
Context Group과 Context에 대한 정보가 출력된다. 추가한 Context (“TestContext”)가 목록에 조회되면 등록이 성공적으로 된 것이다.
다음과 같이 예를 들어보자.
WEBMain.xml의 Context Group에 “/Test” 경로를 가진 Context를 등록하였다(여기서는 명시적인 가상 호스트가 사용되지 않는 것으로 가정한다).
이 Context는 web.xml에 “HeaderTest”라는 Servlet을 가진다.
이 Context가 소속된 Context Group은 HTTP 리스너가 설정되어 있다. 이 리스너는 8088포트를 로컬 머신에서 사용하고 있다.
위의 내용을 가정하고 Context의 HeaderTest Servlet을 다음과 같은 URL을 웹 브라우저에서 실행한다.
http://localhost:8088/Test/HeaderTest
다음과 같은 페이지가 나타난다.
그러므로, Servlet(또는 JSP)를 요청할 때에는 다음과 같은 URL 포맷을 이용한다.
http://<hostname>:<port>/<context request path>/<Servlet/JSP name>
Static Content에 대해서는 다음과 같은 URL 포맷을 사용한다.
http://<hostname>:<port>/<context request path>/<directory path>/<file name>
예를 들어, ”/Test” 요청 경로를 가진 Context의 “static” 디렉터리 아래에 있는 “hello.html”을 호출하기 위해서는 다음과 같은 URL을 사용한다.
http://localhost:8088/Test/static/hello.html
URL 경로의 파일 이름을 지정하지 않으면 기본 페이지인 “index.html”이 사용된다.
요청된 파일 또는 자원이 발견되지 않을 경우에는 다음과 같은 기본 오류 페이지가 반환된다.
“제7장 가상 호스트”에는 Context 요청에 대한 자세한 내용과 가상 호스트에 대해서 설명한다.
jeusadmin과 WebAdmin을 통하여 Context와 Servlet을 모니터링할 수 있다. 모니터링은 WebAdmin을 이용할 것을 권장한다.
jeusadmin에서 디플로이된 Context Group의 정보를 조회하려면 ‘info’를 수행한다.
다음은 특정 Context Group에 대한 정보를 조회하는 방법이다.
info <context group name>
다음은 특정 Context의 정보를 조회하는 방법이다(특정 Servlet에 대한 정보는 조회하지 못한다).
info <context group name> <context name>
더 자세한 정보는 “JEUS Reference Book”의 “4.2.5. 서블릿 엔진 관련 명령어”를 참고한다.
WebAdmin에서 Context 모니터링과 Servlet 모니터링에 관련된 내용을 조회하려면 다음과 같은 방법으로 원하는 Web Context의 상태를 모니터링할 수 있다.
JEUS 노트 트리에서 노드 > 엔진 컨테이너 > container name > 어플리케이션 모듈 > context를 선택한 후 [통계] 탭을 클릭
JEUS 노트 트리에서 노드 > JEUS모니터링을 선택하면 나타나는 MBean 모니터링 화면에서 MBean > JeusManager > J2EEServer > WebModule을 선택한 후 [통계] 탭을 클릭
최대한의 성능을 위해 context의 설정(jeus-web-dd.xml)을 튜닝할 때 다음 같은 사항들을 고려해야 한다.
User log은 버퍼의 크기를 증가시키고 “stdout”의 사용을 자제한다.
JSP Engine은 사용하지 않으면 OFF시킨다.
<enable-reload>와 <check-on-demand> 옵션은 항상 OFF시킨다. 이 옵션은 클래스 변경이 잦은 개발 환경에서만 사용한다.
<max-instance-pool-size>를 “-1”로 하여 SingleThread 된 Servlet에게 최적의 동시성을 제공한다(그러나 잠재적으로 시스템 자원을 낭비하게 된다).
가능하면 파일 Caching 기능을 사용한다. 최대한 많은 Static Content 디렉터리를 파일 Caching 태그에 설정한다. Cache의 <max-idle-time>과 <max-cache-memory>의 값을 크게 설정한다(무한대로 설정해도 무방하다).