9 장. 서블릿 컨테이너 통합하기

이번 장에서는 써드파티 웹 컨테이너를 JBoss 어플리케이션 서버 프레임워크에 통합시키는 과정을 살펴보도록 하겠습니다. 웹 컨테이너란 서블릿과 JSP 페이지로 액세스가 가능한 J2EE 서버 컴포넌트입니다. 가장 널리 사용되고 있는 서블릿 컨테이너는 톰캣이며 현재 JBoss에서 기본 웹 컨테이너로 사용되고 있습니다.

JBoss내에 서블릿 컨테이너를 통합시키는 것은 옵션으로 사용하는 jboss-web.xml 서술자를 사용하여 JBoss의 JNDI 네임스페이스쪽에 web-app.xml JNDI 정보를 매핑시키는 것 뿐만 아니라 JBoss 보안 계층쪽에 인증과 인가까지 위임하는 것으로 구성되어 집니다. 이러한 작업을 단순화하기 위해 org.jboss.web.AbstractWebContainer 클래스가 존재합니다. 이번 장의 초반부에서는 AbstractWebContainer 클래스를 사용하여 웹 컨테이너를 어떻게 통합시키는가에 중점을 두도록 하겠습니다. JBoss/Tomcat 번들에서 보안 소켓 계층(SSL) 암호화의 사용뿐만 아니라 JBoss/Tomcat 번들에 아파치를 사용하도록 설정하는 방법과 같은 설정 토픽을 논의하는 것으로 이번 장을 마무리 하겠습니다.

9.1. AbstractWebContainer 클래스

org.jboss.web.AbstractWebContainer 클래스는 JBoss에 웹 컨테이너를 통합시키기 위한 템플릿 패턴의 구현입니다. JBoss 서버쪽에 자신의 컨테이너를 통합하고자 하는 웹 컨테이너 프로바이더는 AbstractWebContainer의 서브클래스를 생성시키고, 웹 컨테이너에 특정한 셋업과 WAR 배치 단계를 제공해주어야만 합니다. AbstractWebContainer에서는 표준 J2EE web.xml 웹 어플리케이션 배치 서술자 JNDI와 보안 요소들뿐만 아니라 JBoss에 특화된 jboss-web.xml 서술자를 파싱하도록 지원해주어야 합니다. 이러한 배치 서술자들의 파싱은 통합된 JNDI 환경과 보안 컨텍스트를 생성하기 위해 수행되어집니다. 우리는 이미 다른 장에서 jboss-web.xml 서술자를 구성하고 있는 대부분의 요소들을 살펴보았습니다. 그림 9.1, “완전한 jboss-web.xml 서술자 DTD”에서는 참조를 위해 jboss-web.xml 서술자 DTD의 개요를 보여주고 있습니다. 설명과 함께 완전한 DTD에 대한 정보는 JBOSS_DIST/docs/dtd에서 찾을 수 있습니다.

완전한 jboss-web.xml 서술자 DTD

그림 9.1. 완전한 jboss-web.xml 서술자 DTD

논의 되지 않았던 두 개의 요소가 있었으며, 이것은 context-rootvirtual-host 입니다. context-root 요소는 웹 어플리케이션이 위치되어진 곳의 접두사를 지정해주도록 합니다. 이것은 WAR 파일로써 독립된 웹 어플리케이션 배치에서만 가능합니다. EAR내의 일부분으로 포함된 웹 어플리케이션에서는 EAR application.xml 서술자의 context-root 요소를 사용하여 루트를 설정해주어야만 합니다. 예제 9.1, “war를 루트 컨텍스트에 매핑시켜주기 위한 jboss-web.xml 서술자의 샘플”에서는 jboss-web.xml 서술자 샘플에서는 war를 루트 컨텍스트에 매핑하는 방법을 보여주고 있습니다.

예제 9.1. war를 루트 컨텍스트에 매핑시켜주기 위한 jboss-web.xml 서술자의 샘플

<jboss-web>
  <!-- An empty context root map the war to the root context,
       e.g., http://localhost:8080/ -->
  <context-root />
</jboss-web>

virtual-host 요소에서는 웹 어플리케이션이 배치되어져야 하는 가상 호스트의 DNS 이름을 지정합니다. 서블릿 컨텍스트에 대해 가상 호스트를 설정하는 세부사항은 특정 서블릿 컨테이너에 따라 다릅니다. 이번 장의 후반부에서 톰캣 서블릿 컨테이너를 살펴보면서 virtual-host 요소의 사용 예를 다루겠습니다.

9.1.1. AbstractWebContainer 계약(Contract)

AbstractWebContainer는 배치될 필요성이 있는 설치된 war 파일들의 작업을 위임하기 위해 JBoss J2EE 배치자에서 사용되는 org.jboss.web.AbstractWebContainerMBean 인터페이스의 구현 추상 클래스입니다. 아랫쪽에서 AbstractWebContainer의 몇몇 핵심 메쏘드들을 살펴보도록 하겠습니다.

public boolean accepts(DeploymentInfo sdi)
{
    String warFile = sdi.url.getFile();
    return warFile.endsWith("war") || warFile.endsWith("war/");
}

accepts 메쏘드는 허용되는 배치 형태를 지정하기 위해 JBoss 배치자에서 구현되어집니다. AbstractWebContainer는 JARs 나 풀려진 디렉터리로써 WARs의 패치를 처리합니다.

public synchronized void start(DeploymentInfo di) 
    throws DeploymentException
{
    Thread thread = Thread.currentThread();
    ClassLoader appClassLoader = thread.getContextClassLoader();

    try {
        // 고유한 ENC를 보장하기 위해 war에 대한 클래스로더를 생성
        URL[] empty = {};
        URLClassLoader warLoader = URLClassLoader.newInstance(empty, di.ucl);
        thread.setContextClassLoader(warLoader);
        WebDescriptorParser webAppParser = new DescriptorParser(di);

        String webContext = di.webContext;
        if (webContext != null) {
            if (webContext.length() > 0 && webContext.charAt(0) !=
                '/') {
                webContext = "/" + webContext;
            }
        }

        // war URL 가져오기
        URL warURL = di.localUrl != null ? di.localUrl : di.url;
        if (log.isDebugEnabled()) {
            log.debug("webContext: " + webContext);
            log.debug("warURL: " + warURL);
            log.debug("webAppParser: " + webAppParser);
        }

        // web.xml 과 jboss-web.xml 서술자들을 파싱하기 
        WebMetaData metaData = (WebMetaData) di.metaData;
        parseMetaData(webContext, warURL, di.shortName, metaData);

        WebApplication warInfo = new WebApplication(metaData);
        warInfo.setDeploymentInfo(di);
        performDeploy(warInfo, warURL.toString(), webAppParser);
        deploymentMap.put(warURL.toString(), warInfo);

        // 시작을 위한 이벤트 생성
        super.start(di);
    } catch(DeploymentException e) {
        throw e;
    } catch(Exception e) {
        throw new DeploymentException("Error during deploy", e);
    } finally {
        thread.setContextClassLoader(appClassLoader);
    }
}

이번 섹션은 start 메쏘드에 대응됩니다. 이 메쏘드는 템플릿 패턴 메쏘드의 구현입니다. deploy 메쏘드에 넘겨주는 인수는 WAR 배치 정보 객체입니다. 여기에는 WAR에 대한 URL, WAR를 위한 UnifiedClassLoader, EAR과 같은 부모 아카이브 그리고 WAR가 EAR의 일부분일 경우 J2EE application.xml context-root가 포함됩니다.

start 메쏘드의 첫 단계는 현재의 쓰레드 컨텍스트 클래스 로더를 저장하고 자신의 부모로써 WAR UnifiedClassLoader를 사용하여 또 다른 URLClassCloader(warLoader)를 생성하는 것입니다. 이 warLoader는 생성되어질 WAR에 대한 고유한 JNDI ENC(enterprise naming context)를 보장하기 위해 사용되어집니다. java:comp 컨텍스트의 유일성이 java:comp 컨텍스트를 생성하는 클래스 로더에 의해 결정된다는 것을 3 장, Naming on JBoss에서 언급했었습니다. warLoader 클래스로더(ClassLoader)는 performDeploy 호출이 만들어지기전에 현재 쓰레드 컨텍스트 클래스 로더로 설정됩니다. 다음 단계에서 web.xmljboss-web.xml 서술자는 parseMetaData를 호출하여 파싱되어집니다. 그런 다음, performDeploy 호출을 통해 WAR의 실제 배치를 수행하기 위해 웹 컨테이너에 특정한 서브클래스가 호출되어집니다. 이러한 배치에 대한 WebApplication 객체는 키로써 warUrl을 사용하여 배치된 어플리케이션 맵내에 저장됩니다. 마지막 단계에서는 메쏘드의 시작시에 존재하였던 하나로 쓰레드 컨텍스트 클래스 로더를 복원시키는 것입니다.

protected abstract void performDeploy(WebApplication webApp, String warUrl,
                                      WebDescriptorParser webAppParser) 
    throws Exception;

이것은 추상적인 performDeploy 메쏘드의 사용방법입니다. 이 메쏘드는 start 메쏘드에 의해 호출되고 웹 컨테이너에 특화된 배치 단계를 수행하기 위해 서브클래스에 의해 덮어씌여져야만 합니다. WebApplication은 인수로써 제공되며, 여기에는 web.xml 서술자와 jboss-web.xml 서술자로부터 메터데이터를 포함합니다. 메터데이터에는 J2EE application.xml 서술자로부터 웹 모듈에 대한 context-root를 포함하거나, 이것이 독립 배치인 경우 jboss-web.xml 서술자를 포함합니다. 또한 메터데이터에는 jboss-web.xml 서술자의 virtual-host 값도 포함합니다. performDeploy로부터 반환되면서, WebApplication은 반드시 배치를 위한 서블릿 컨텍스트의 클래스 로더가 함께 만들어져야만 합니다. warUrl 인수는 배치될 웹 어플리케이션 WAR의 URL에 대한 문자열입니다. webAppParser 인수는 웹 어플리케이션 JNDI 환경을 설정하기 위한 parseWebAppDescriptors 메쏘드를 호출하기 위해 사용되어져야만 하는 서브클래스를 처리하는 콜백(callback)입니다. 이 콜백에서는 WAR의 구동시 로딩되어지는 생성되는 서블릿 이전에 웹 어플리케이션 JNDI 환경을 구성하는 서브클래스에 대한 훅(hook)을 제공합니다. 서브클래스의 performDeploy 메쏘드 구현은 순서대로 정렬될 필요가 있기 때문에 EJB, 리소스 팩토리등과 같은 JBoss의 리소스에 대한 JNDI로의 액세스에 필요한 어떠한 서블릿의 시작 이전에 parseWebAppDescriptors를 호출할 수 있습니다. 서브클래스 구현에 의해 처리되어질 필요가 있는 중요한 한가지 설정의 세부사항은 생성되어진 어떠한 웹 컨테이너에 특화된 클래스 로더에 대한 부모 클래스 로더로써 현재의 쓰레드 컨텍스트 클래스 로더를 사용하는 것입니다. 이것이 실패하면, 웹 어플리케이션에서 EJB나 JBoss 리소스를 JNDI ENC를 통해 액세스하려는 시도에 문제가 발생합니다.

public synchronized void stop(DeploymentInfo di)
    throws DeploymentException
{
    URL warURL = di.localUrl != null ? di.localUrl : di.url;
    String warUrl = warURL.toString();
    try {
        performUndeploy(warUrl);
        // 웹 어플리케이션 ENC 제거...
        deploymentMap.remove(warUrl);
        // 중지를 위한 이벤트 생성
        super.stop(di);
    } catch(DeploymentException e) {
        throw e;
    } catch(Exception e) {
        throw new DeploymentException("Error during deploy", e);
    }
}

이것은 stop 메쏘드입니다. 컨테이너에 특화된 배치제거(undeployment) 단계를 수행하기 위한 서브클래스 performUndeploy 메쏘드를 호출합니다. 어플리케이션의 배치를 제거한 후, warUrl은 배치 맵으로부터 등록이 해제됩니다. warUrl 인수는 performDeploy 메쏘드쪽에 최초 넘겨진 WAR의 URL 문자열입니다.

protected abstract void performUndeploy(String warUrl) 
    throws Exception;

이것은 performUndeploystop 메쏘드로부터 호출되어집니다. performUndeploy를 호출하는 것은 웹 컨테이너에 특화된 배치제거 단계를 수행하는 서브클래스의 호출입니다.

public void setConfig(Element config)
{
}

setConfig 메쏘드는 MBean 속성을 통해 덮어쓰기가 가능한 서브클래스가 임의의 확장된 설정을 지원하기 원하는 경우 덮어쓸 수 있는stub 메쏘드입니다. config 인수는 웹 컨테이너 서비스의 jboss-service.xml 서술자의 mbean 요소 사양서내에 들어가 있는 Config 속성의 자식 요소에 의해 주어지는 임의의 계층에 대한 부모 DOM 요소입니다. 여러분은 톰캣을 JBoss내에 임베딩하도록 지원하는 MBean을 살펴보면서 이 메쏘드와 config 값을 사용하는 예를 볼 수 있을 것입니다.

protected void parseWebAppDescriptors(DeploymentInfo di, 
                                      ClassLoader loader,
                                      WebMetaData metaData)
    throws Exception
{
    log.debug("AbstractWebContainer.parseWebAppDescriptors, Begin");
    InitialContext iniCtx = new InitialContext();
    Context envCtx = null;
    Thread currentThread = Thread.currentThread();
    ClassLoader currentLoader = currentThread.getContextClassLoader();
    try {
        // 웹 어플리케이션에 대한 고유한 java:comp/env 환경 생성
        log.debug("Creating ENC using ClassLoader: "+loader);
        ClassLoader parent = loader.getParent();
        while (parent != null ) {
            log.debug(".."+parent);
            parent = parent.getParent();
        }
        currentThread.setContextClassLoader(loader);
        metaData.setENCLoader(loader);
        envCtx = (Context) iniCtx.lookup("java:comp");
        // 전역 트랜잭션 관리자에 링크 추가 
        envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
        log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
        envCtx = envCtx.createSubcontext("env");
    } finally {
        currentThread.setContextClassLoader(currentLoader);
    }
                
    Iterator envEntries = metaData.getEnvironmentEntries();
    log.debug("addEnvEntries");
    addEnvEntries(envEntries, envCtx);
                
    Iterator resourceEnvRefs = metaData.getResourceEnvReferences();
    log.debug("linkResourceEnvRefs");
    linkResourceEnvRefs(resourceEnvRefs, envCtx);

    Iterator resourceRefs = metaData.getResourceReferences();
    log.debug("linkResourceRefs");
    linkResourceRefs(resourceRefs, envCtx);
              
    Iterator ejbRefs = metaData.getEjbReferences();
    log.debug("linkEjbRefs");
    linkEjbRefs(ejbRefs, envCtx, di);
                
    Iterator ejbLocalRefs = metaData.getEjbLocalReferences();
    log.debug("linkEjbLocalRefs");                
    linkEjbLocalRefs(ejbLocalRefs, envCtx, di);
               
    String securityDomain = metaData.getSecurityDomain();
    log.debug("linkSecurityDomain");
    linkSecurityDomain(securityDomain, envCtx);
                
    log.debug("AbstractWebContainer.parseWebAppDescriptors, End");
 }
 

parseWebAppDescriptors 메쏘드는 web.xml 서술자내에 선언되어진 웹 어플리케이션 ENC (java:comp/env) env-entry, resource-env-ref, resource-ref, local-ejb-refejb-ref 값을 설정하기 위해 webAppParser.parseWebAppDescriptors 콜백을 호출할 때 서브클래스 performDeploy 메쏘드내에서부터 호출되어집니다. env-entry 값을 생성하기 위해 jboss-web.xml 서술자는 필요하지 않습니다. resource-env-ref, resource-refejb-ref 요소의 생성에 배치된 리소스/EJB의 JNDI 이름을 위한 jboss-web.xml 서술자는 필요하지 않습니다. ENC 컨텍스트가 웹 어플리케이션에 대해 공개적이지 않은(private) 이유로, 웹 어플리케이션 클래스 로더는 ENC 식별에 사용됩니다. loader 인수는 웹 어플리케이션에 대한 클래스 로더로써 널값아 아닐 수도 있습니다. metaData 인수는 서브클래스 performDeploy 메쏘드에 넘겨진 WebMetaData 인수입니다. parseWebAppDescriptors의 구현은 WAR 배치 서술자로부터 가져온 메터데이터 정보를 사용하고 JNDI ENC 바인딩을 생성합니다.

 protected void addEnvEntries(Iterator envEntries, Context envCtx)
    throws ClassNotFoundException, NamingException
{
}

addEnvEntries 메쏘드는 web.xml 서술자에서 지정하였던 java:comp/env 웹 어플리케이션 env-entry 바인딩을 생성합니다.

protected void linkResourceEnvRefs(Iterator resourceEnvRefs, Context envCtx)
    throws NamingException
{
}

linkResourceEnvRefs 메쏘드는 java:comp/env/xxx 웹 어플리케이션 JNDI ENC resource-env-ref web.xml 서술자 요소를 jboss-web.xml 서술자내에서 지정한 매핑을 사용하여 배치된 JNDI 이름들로 매핑시킵니다.

protected void linkResourceRefs(Iterator resourceRefs, Context envCtx)
    throws NamingException
{
}

linkResourceRefs 메쏘드는 java:comp/env/xxx 웹 어플리케이션 JNDI ENC resource-ref web.xml 서술자 요소를 jboss-web.xml 서술자내에서 지정한 매핑을 사용하여 배치된 JNDI 이름들로 매핑시킵니다.

protected void linkEjbRefs(Iterator ejbRefs, Context envCtx, DeploymentInfo di)
    throws NamingException
{
}

linkEjbRefs 메쏘드는 java:comp/env/ejb 웹 어플리케이션 JNDI ENC ejb-ref web.xml 서술자 요소를 jboss-web.xml 서술자내에서 지정한 매핑을 사용하여 배치된 JNDI 이름들로 매핑시킵니다.

protected void linkEjbLocalRefs(Iterator ejbRefs, Context envCtx,
                                DeploymentInfo di)
    throws NamingException
{
}

linkEjbLocalRefs 메쏘드는 java:comp/env/ejb 웹 어플리케이션 JNDI ENC ejb-local-ref web.xml 서술자 요소를 jboss-web.xml 서술자내에서 지정한 ejb-link 매핑을 사용하여 배치된 JNDI 이름들로 매핑시킵니다.

protected void linkSecurityDomain(String securityDomain, Context envCtx)
    throws NamingException
{
}

linkSecurityDomain 메쏘드는 AuthenticationManager 구현쪽을 가르키는 securityMgr 바인딩과 RealmMapping 구현을 가르키는 RealmMapping 바인딩을 포함하고 있는 웹 어플리케이션에 대한 보안 도메인과 관련된 java:comp/env/security 컨텍스트를 생성합니다. 또한 요청 쓰레드와 관련된 인증된 Subject로의 동적인 액세스를 지원하는 subject 바인딩을 생성합니다. jboss-web.xml 서술자가 security-domain 요소를 포함하는 경우, 바인딩은 security-domain 요소에 의해 지정된 JNDI 이름에 대한 javax.naming.LinkRef 혹은 이 이름의 subcontext 입니다. 만일 security-domain 요소가 없다면, 바인딩은 단순히 모든 인증과 인가 점검을 허용하는 org.jboss.security.plugins.NullSecurityManager 인스턴스가 됩니다.

public String[] getCompileClasspath(ClassLoader loader)
{
}

getCompileClasspath 메쏘드는 주어진 로더에서 클래스 로더 체인을 시작하도록 하고 URL 문자열의 완전한 클래스경로를 구성하도록 기여하는 URL에 대한 각각의 클래스 로더를 질의하는 클래스 경로를 생성하는 웹 컨테이너를 위한 사용가능한 유틸리티 메쏘드입니다. 이것은 편집을 위해 주어진 완전한 클래스경로가 예상되는 몇몇 JSP 컴파일러 구현(Jasper가 이에 해당됨)에서 필요로 합니다.

9.1.2. AbstractWebContainer 서브클래스 생성하기

웹 컨테이너를 JBoss로 통합하기위해 여러분은 AbstractWebContainer의 서브클래스를 생성하고 앞 섹션에서 언급했던 것 처럼 필요한 performDeploy(WebApplication, String, WebDescriptorParser) 와 tt class="literal">performUndeploy(String) 메쏘드를 구현합니다. 다음에 오는 추가적인 통합에 대한 항목들 또한 함께 고려해주어야만 합니다.

9.1.2.1. 쓰레드 컨텍스트 클래스 로더의 사용

이것에 대해서는 performDeploy 메쏘드를 설명하면서 언급했지만, 매우 민감한 사항이므로 여기서 다시 한번더 논의하도록 하겠습니다. WAR 컨테이너의 셋업과정중에, 현재 쓰레드 컨텍스트 클래스 로더는 생성되어지는 웹 컨테이너에 특화된 클래스 로더를 위한 부모 클래스 로더로써 사용되어져야만 합니다. 이것이 실패하면 JNDI ENC를 통해 EJB나 JBoss 리소스에 액세스하려는 웹 어플리케이션의 시도에 문제가 발생되어집니다.

9.1.2.2. log4j를 사용한 로깅 통합하기

JBoss에서는 자신의 내부 로깅 API로써 아파치의 log4j 로깅 API를 사용합니다. 웹 컨테이너를 JBoss와 잘 통합하기 위해서는 웹 컨테이너의 로깅 작업(abstraction)과 log4j API간의 매핑을 제공할 필요가 있습니다. AbstractWebContainer의 서브클래스로써 여러분의 통합 클래스는 super.log 인스턴스 변수를 통해 log4j 인터페이스로 액세스하거나 이와 동일한 작업을 하는 슈퍼클래스 getLog() 메쏘드를 갖습니다. 이것은 log4j 카테고리를 감싸는 org.jboss.logging.Logger 클래스의 인스턴스입니다. log4j 카테고리의 이름은 컨테이너 서브클래스의 이름입니다.

9.1.2.3. 웹 컨테이너의 인증과 인가를 JBossSX로 위임하기

이상적으로 보면, 웹 어플리케이션과 EJB의 인증 및 인가 모두는 동일한 보안 관리자에 의해 처리되는 것입니다. 여러분의 웹 컨테이너에 대해 이것이 가능하게 하려면, 여러분은 반드시 JBoss 보안 계층에 후킹을 해야 합니다. 이를 위해서는 일반적으로 웹 컨테이너의 보안 콜아웃(callout)을 JBoss의 보안 API로 매핑해주는 요청 인터셉터가 필요합니다. JBossSX 보안 프레임워크와의 통합은 앞 섹션에서 나왔던 linkSecurityDomain 메쏘드에서 설명한 것 처럼 java:comp/env/security 컨텍스트의 수립(establishment)에 기반합니다. 보안 컨텍스트에서는 서브클래스의 요청 인터셉터에 의해 사용되는 웹 어플리케이션과 관련된 JBossSX 보안 관리자 인터페이스 구현에 액세스하는 것을 제공합니다. 보안 컨텍스트를 사용하여 사용자를 인증하기 위한 단계의 아웃라인은 준(quasi) 슈도-코드로 구성된 예제 9.2, “JBossSX API와 java:comp/env/security JNDI 컨텍스트를 통해 사용자를 인증하는(authenticating) 슈도-코드의 설명”에 보여지고 있습니다. 예제 9.3, “JBossSX API와 java:comp/env/security JNDI 컨텍스트를 통해 사용자를 인가(authorization)하는 슈도-코드의 설명”에서는 사용자의 허가에 대해 동등한 과정(process)를 제공합니다.

예제 9.2. JBossSX API와 java:comp/env/security JNDI 컨텍스트를 통해 사용자를 인증하는 슈도-코드의 설명.

// 요청 컨텍스트로부터 사용자명과 암호를 가져옴...
HttpServletRequest request = ...;
String username = getUsername(request);
String password = getPassword(request);

// ENC 컨텍스트로부터 JBoss의 보안 관리자를 가져옴
InitialContext iniCtx = new InitialContext();
AuthenticationManager securityMgr = (AuthenticationManager)
    iniCtx.lookup("java:comp/env/security/securityMgr");

SimplePrincipal principal = new SimplePrincipal(username);
if (securityMgr.isValid(principal, password)) {
    // 사용자가 웹 컨텐츠로 액세스가 허용됨을 가리킴...
    // 서블릿에 의해 만들어진 호출에 대해 사용자 정보를 JBoss로 전파(Propagate)
    SecurityAssociation.setPrincipal(principal);
    SecurityAssociation.setCredential(password.toCharArray());
} else {
    // 액세스 거부...
}

예제 9.3. JBossSX API와 java:comp/env/security JNDI 컨텍스트를 통해 사용자를 인가(authorization)하는 슈도-코드의 설명.

// 요청 컨텍스트로부터 사용자명과 요구되는 역할을 가져옴...
HttpServletRequest request = ...;
String username = getUsername(request);
String[] roles = getContentRoles(request);

// ENC 컨텍스트로부터 JBoss의 보안 관리자를 가져옴
InitialContext iniCtx = new InitialContext();
RealmMapping securityMgr = (RealmMapping)
    iniCtx.lookup("java:comp/env/security/realmMapping");

SimplePrincipal principal = new SimplePrincipal(username);
Set requiredRoles = new HashSet(java.util.Arrays.asList(roles));

if (securityMgr.doesUserHaveRole(principal, requiredRoles)) {
    // 웹 컨텐츠에 대해 요구되는 역할을 갖는 사용자를 기리킴...
} else {
    // 액세스 거부...
}

9.2. JBoss/Tomcat-5 번들 문서

이번 섹션에서 우리는 JBoss/Tomcat 5 통합 번들의 경우에 대한 특정한 설정 문제들을 논의해 보고자 합니다. 톰캣 5는 아파치 자바 서블릿 컨테이너의 최근 버전입니다. 이 버전에서는 서블릿 2.4 와 JSP 2.0 사양서를 지원합니다. JBoss/Tomcat 통합 계층은 JBoss MBean 서비스 설정에 의해 제어됩니다. 톰캣 5 버전의 웹 컨테이너를 내장하는데 사용한 MBean은 org.jboss.web.tomcat.tc5.Tomcat5 이며, 이것은 AbstractWebContainer 클래스의 서브클래스입니다. 이것의 설정가능한 속성은 다음과 같습니다:

  • Java2ClassLoadingCompliance: WAR로부터 로딩되는 모델을 서블릿 모델이 아니라 표준 Java2 부모 위임 클래스 로딩 모델이 될 수 있도록 합니다. 클래스 로딩시 충돌때문에 EJB에 의해 사용되는 클래스를 갖는 클라이언트 JAR를 포함하고 있는 WAR로부터 로딩되는 것이 기본적인 특성이기 때문에 이것은 true 입니다. 이 값을 false로 설정하여 서블릿 클래스 로딩 모델을 가능하게 하면, 여러분은 배치 패키지를 배치할 때 클래스의 중복을 피하도록 구성해주어야할 필요가 있습니다.
  • UseJBossWebLoader: 웹 어플리케이션 클래스 로더로써 톰캣을 사용하는 클래스 로더가 JBoss의 unified 클래스 로더인지를 가르키는 플래그값입니다. 기본값은 true로써 WAR내의 WEB-INF/classesWEB-INF/lib 내의 사용가능한 클래스들이 2 장, JBoss JMX 마이크로커널에서 논의하였던 기본 공유된 클래스 로더 레포지터리에 합쳐집니다. 이것은 기본 서블릿 클래스 로딩 모델에 반대가 되는 것으로 여러분이 원하는 것은 아니며 웹 어플리케이션간의 클래스/리소스 공유 결과를 초래합니다. 여러분은 이 속성을 false로 설정함으로써 이것을 비활성화시킬 수 있습니다.
  • ManagerClass: 이것은 배포가능한 것으로 표시된 웹 어플리케이션의 상태를 복사하기 위한 세션 관리자로 사용되는 클래스입니다. 세션 관리자 구현을 제공하는 것은 오직 org.jboss.web.tomcat.tc5.session.JBossCacheManager 뿐이며, 이를 사용하여 JBossCache는 배포된 상태를 추적하게 됩니다.
  • SnapshotMode: 클러스터링된 환경내에서의 스냅샷 모드를 설정합니다. 이것은 반드시 instantinterval 둘중에 하나이어야만 합니다. instant 모드는 수정이 발생되면 클러스터링된 세션에 곧바로 변경사항을 전파합니다. interval 모드에서는 모든 수정사항들은 SnapshotInterval에서 설정한 주기값에 따라 주기적으로 전달되어집니다.
  • SnapshotInterval: interval 스냅샷 모드에서 밀리세컨즈단위로 스냅샷 전달 간격을 지정합니다. 기본값은 1000 ms로, 1초입니다.

9.2.1. 톰캣의 server.xml 파일

jboss-service.xml 파일이 JBoss/Tomcat 통합을 제어하는 반면, 톰캣은 자신의 동작을 가이드해주는 설정 파일을 갖습니다. 이것이 바로 server.xml 서술자로써 여러분은 deploy/jbossweb-tomcat50.sar 디렉터리에서 찾을 수 있습니다.

server.xml 파일에서 제공하는 톰캣 5 설정 DTD의 개요.

그림 9.2. server.xml 파일에서 제공하는 톰캣 5 설정 DTD의 개요.

우리는 이제 server.xml 파일내에서 사용이 가능한 설정 옵션들 몇가지를 살펴볼 것입니다. 최상위 레벨 요소는 Server 로써, 이것이 루트 요소가 됩니다. 이 요소는 전체 웹의 하부시스템을 표현해주는 한개의 Service 요소를 포함해야만 합니다. 이 요소에서 지원되는 속성은 오직 하나입니다:

  • name: 서비스가 알려진 고유한 이름.

9.2.1.1. Connector

Connector 요소는 Service에 연관되어 있는 클라이언트에서 요청과 수신이 가능하도록 하는 전송 메커니즘을 설정합니다. Connector는 요청을 엔진쪽으로 포워딩시키고 그 결과를 요청하는 클라이언트쪽에 반환합니다. Connector는 다음과 같은 속성들을 지원합니다:

  • enableLookups: ServletRequest.getRemoteHost 메쏘드를 통해 액세스가 가능한 클라이언트의 호스트명을 DNS 해석을 통해 얻을 수 있는지를 가르키는 플래그입니다. 이 플래그의 기본값은 false 입니다. 이것은 DNS 해석을 하도록 하면, 성능적인 측면에 영향을 미치기 때문입니다.
  • redirectPort: 신뢰할 수 있는 전송이거나 보전성이 강제(integrity constraint)되는 안전한 컨텐츠의 요청이 수신되는 경우, 비-SSL 요청이 리다이렉트되어지는 포트번호입니다. 기본값은 표준 HTTPS 포트인 443 입니다.
  • secure: 전송 채널이 보호되었는지를 가르키는 ServletRequest.isSecure 메쏘드 값 플래그를 설정합니다. 이 플래그의 기본값은 false 입니다.
  • scheme: 프로토콜의 이름을 ServletRequest.getScheme 메쏘드에 의해 액세스되어지는 것으로 설정합니다. 기본 스키마는 http 입니다.
  • acceptCount: 모든 가능한 요청 처리 쓰레드가 사용되어지는 경우 들어오는 연결 요청에 대한 최대 큐의 길이입니다. 큐가 꽉 찬후에 수신되는 요청들은 거절됩니다. 기본값은 10 입니다.
  • address: IP 주소를 하나 이상 갖는 서버의 경우, 이 속성은 지정된 포트번호상에서 수신에 사용이 되는 주소를 지정합니다. 기본적으로 이 포트는 서버에 연결된 모든 IP 주소를 사용하게 됩니다.
  • bufferSize: 커넥터에 의해 생성된 input 스트림을 위해 제공되어지는 버퍼의 크기(바이트단위) 입니다. 기본값은 2048 바이트의 버퍼가 제공됩니다.
  • connectionTimeout: 제출된 요청 URI 라인에 대해 수신이 접수된 이후, 커넥터가 대기하게될 시간을 밀리세컨즈 단위로 지정합니다. 기본값은 60000(즉, 60초) 입니다.
  • debug: 이 컴포넌트에 의해 셩성되는 로그 메시지의 디버깅 수준으로, 높은 번호일 수록 보다 많은 상세한 정보를 출력하게 됩니다. 지정하지 않으면, 이 속성은 영(0)으로 설정됩니다. 이것이 로그에 반영될지 안될지는 log4j 카테고리의 org.jboss.web.tomcat.tc5.Tomcat5 threshold 에 의존적입니다.
  • maxThreads: 이 커넥터에 의해 생성되어질 요청 처리 쓰레드의 최대 갯수이기 때문에 처리할 수 있는 최대 동시 요청 갯수를 결정하게 됩니다. 지정하지 않는다면 이 속성은 200 으로 설정됩니다.
  • maxSpareThreads: 불필요한 쓰레드를 중지시키는 쓰레드 풀을 시작시키기 이전까지 존재하도록 허용하게 되는 사용하지 않는 요청 처리 쓰레드의 최대 갯수입니다. 기본값은 50입니다.
  • minSpareThreads: 이 커넥터가 처음 시작되었을 때 생성시키게 되는 요청 처리 쓰레드의 갯수입니다. 또한 커넥터는 사용가능한 휴지(idle)상태의 처리 쓰레드의 갯수도 지정하도록 할 수 있습니다. 이 속성은 maxThreads에서 설정된 값보다 적게 설정되어져야만 합니다. 기본값은 4입니다.
  • port: 이 커넥터가 서버 소켓을 생성하고 들어오는 요청들을 대기하는 TCP 포트의 번호입니다. 여러분의 운영 시스템은 특정한 IP 주소에 대한 하나의 포트 번호에 대해 하나의 서버 어플리케이션만을 수신하도록 허용할 것입니다.
  • proxyName: 이 커넥터가 프록시 설정으로 사용되는 경우, 이 속성을 request.getServerName() 호출에 대해 리턴되어질 서버의 이름으로 지정하도록 설정합니다.
  • proxyPort: 이 커넥터가 프록시 설정으로 사용되는 경우, 이 속성을 request.getServerPort() 호출에 대해 리턴되어질 서버의 포트로 지정하도록 설정합니다.
  • tcpNoDelay: true로 설정한다면, TCP_NO_DELAY 옵션은 서버의 소켓에 대해 설정되어지게 됩니다. 이를 통해 대부분의 경우 성능을 향상시킬 수 있습니다. 기본값은 true로 설정되어 있습니다.

추가적인 속성의 설명은 톰캣 웹사이트 문서쪽에서 찾을 수 있습니다: http://jakarta.apache.org/tomcat/tomcat-5.0-doc/config/http.html

9.2.1.2. Engine

각각의 Service는 하나의 Engine 설정을 가져야만 합니다. Engine은 설정된 커넥터를 통해 서비스쪽으로 전송되는 요청을 처리합니다. 내장된 서비스에서 지원되는 자식 요소들에는 Host, Logger, DefaultContext, ValveListener가 있습니다. 지원되는 속성들은 다음과 같습니다:

  • className: 사용하려는 org.apache.catalina.Engine 인터페이스 구현의 완전한 형태를 갖는 클래스의 이름. 이 값을 지정하지 않으면, 기본값은 org.apache.catalina.core.StandardEngine 입니다.
  • defaultHost: Host 설정과 일치하지 않는 호스트이름을 갖는 요청을 처리하는 Engine에서 설정된 Host의 이름.
  • name: Engine을 할당하는 논리적인 이름. 이것은 Engine에서 생성하는 로그 메시지내에 사용되어집니다.

Engine 요소에 대한 추가적인 정보는 톰캣 웹사이트 문서에서 찾을 수 있습니다: http://jakarta.apache.org/tomcat/tomcat-5.0-doc/config/engine.html.

9.2.1.3. Host

Host 요소는 가상 호스트 설정을 표현합니다. 이것은 지정된 DNS 호스트명을 갖는 웹 어플리케이션을 위한 컨테이너입니다. 내장된 서비스에서 지원되는 자식 요소들은 Alias, Logger, DefaultContext, Valve and Listener가 있습니다. 지원되는 속성들은 다음과 같습니다:

  • className: 사용하려는 org.apache.catalina.Host 인터페이스 구현의 완전한 형태를 갖는 클래스의 이름. 지정하지 않으면, 기본적으로 org.apache.catalina.core.StandardHost가 사용됩니다.
  • name: 가상 호스트의 DNS 이름. 최소한 하나의 Host 요소가 포함되어 있는 EnginedefaultHost 값에 일치하는 이름을 갖도록 설정되어져야만 합니다.

Alias 요소는 Host 요소의 자식 요소 옵션입니다. 각각의 Alias 컨텐츠에서는 Host를 끝맺기 위해 교환되는 DNS 이름을 지정합니다.

Host 요소의 추가적인 정보는 톰캣 웹사이트 문서에서 찾을 수 있습니다: http://jakarta.apache.org/tomcat/tomcat-5.0-doc/config/host.html.

9.2.1.4. DefaultContext

DefaultContext 요소는 웹 어플리케이션 컨텍스트를 위한 설정 템플릿입니다. 이것은 Engine 이나 Host 레벨에서 정의되어질 수 있습니다. 내장된 서비스에서 지원되는 자식 요소들에는 WrapperLifecycle, InstanceListener, WrapperListener, 그리고 Manager가 있습니다. 지원되는 속성들은 다음과 같습니다:

  • className: org.apache.catalina.core.DefaultContext 구현의 완전한 형태를 갖는 클래스의 이름. 기본값은 org.apache.catalina.core.DefaultContext 이며, 덮어쓰여질 값은 반드시 DefaultContext의 서브클래스이어야만 합니다.
  • cookies: 세션이 쿠키를 사용하여 추적하게 되는지를 가르키는 플래그값입니다. 기본값은 true 입니다.
  • crossContext: ServletContext.getContext(String path) 메쏘드가 호출하는 웹 어플맄이션의 가상 호스트내에 배치된 다른 웹 어플리케이션에 대한 컨텍스트를 리턴해줘야하는 지를 가르키는 플래그 값입니다. 기본값은 false 입니다.

9.2.1.5. Logger

Logger 요소에서는 톰캣 인스턴스의 로깅 설정을 지정합니다. 지원되는 속성들은 다음과 같습니다:

  • className: org.apache.catalina.Logger 인터페이스 구현의 완전한 형태를 갖는 클래스 이름. JBoss 로깅과 통합하기 위해 이것이 설정되어져야만 합니다.
  • verbosity: 기본 로그 레벨.
  • category:기본 로그 카테고리.

9.2.1.6. Valve

Valve 요소는 웹 컨테이너를 위한 요청 처리 파이프라인에 훅킹을 설정합니다. Valve는 반드시 org.apache.catalina.Valve 인터페이스를 구현해야 합니다. 오직 한개의 설정 속성만을 필요로 합니다:

  • className: org.apache.catalina.Valve 인터페이스 구현의 완전한 형태를 갖는 클래스 이름.

가장 흔하게 사용되는 Valve는 들어오는 요청의 표준 HTTP 액세스 로그를 유지하는 AccessLogValve 입니다. 액세스 로그 값에 대한 classNameorg.jboss.web.catalina.valves.AccessLogValue 입니다. 이것에서 지원하는 추가 값 속성들은 다음과 같습니다:

  • directory: 액세스 로그 파일이 생성되어질 디렉터리의 경로.
  • pattern: 로그 메시지의 포맷을 정의하는 패턴 지시자(specifier). 기본값은 common 입니다.
  • prefix: 각 로그 파일의 이름에 추가되는 접두사. 기본값은 access_log 입니다.
  • suffix: 각 로그 파일의 이름에 추가되는 접미사. 기본값은 빈 문자열로써 접미사를 붙이지 않습니다.

Valve 요소에 대한 추가적인 정보와 사용가능한 값의 구현은 톰캣 웹 사이트 문서에서 찾을 수 있습니다: http://jakarta.apache.org/tomcat/tomcat-5.0-doc/config/valve.html.

9.2.2. JBoss/Tomcat 번들에서 SSL 사용하기

내장된 톰캣 서블릿 컨테이너에서 SSL을 통해 HTTP를 설정하는 방법이 몇가지 있습니다. JBossSX SecurityDomain으로부터 JSSE 서버 인증 정보를 얻어올 수 있게 하는 JBoss에 특화된 커넥터 소켓 팩토리를 사용하는가와 안하는가가 가장 큰 차이점입니다. 이것은 org.jboss.security.plugins.JaasSecurityDomain MBean을 사용하여 SecurityDomain을 성립하는 것이 필요합니다. 8 장, JBoss에서의 보안의 SSL 암호화를 갖춘 RMI 가능하게 하기에서 우리가 사용했던 절차와 유사한 2 단계로 구성됩니다. 이러한 방법을 통해 오직 하나의 SSL 커넥터 설정을 보여주는 server.xml 설정 파일이 예제 9.4, “톰캣 5에서 주 커넥터 프로토콜로써 SSL을 사용하도록 설정하는 JaasSecurityDoman 과 EmbeddedCatalinaSX MBean 설정”에 보여지고 있습니다. 이 설정은 8 장, JBoss에서의 보안처럼 동일한 JaasSecurityDomain 설정을 포함하지만, 서술자(descriptor)가 chap8.keystore를 포함하는 SAR의 일부분으로 배치되어지지 않기 때문에 여러분이 직접 chap8.keystoreserver/default/conf 디렉터리에 복사해줄 필요가 있습니다.

예제 9.4. 톰캣 5에서 주 커넥터 프로토콜로써 SSL을 사용하도록 설정하는 JaasSecurityDoman 과 EmbeddedCatalinaSX MBean 설정.

<Server>
    <Service name="jboss.web" className="org.jboss.web.tomcat.tc5.StandardService">

        <Connector port="8080" address="${jboss.bind.address}" maxThreads="150"
            minSpareThreads="25" maxSpareThreads="75" enableLookups="false"
            redirectPort="443" acceptCount="100" connectionTimeout="20000" 
            disableUploadTimeout="true"/>

        <Connector port="443" address="${jboss.bind.address}" maxThreads="100"
            minSpareThreads="5" maxSpareThreads="15" scheme="https"
            secure="true" clientAuth="false"
            keystoreFile="${jboss.server.home.dir}/conf/chap8.keystore"
            keystorePass="rmi+ssl" sslProtocol="TLS"/>

        <Engine name="jboss.web" defaultHost="localhost">
            <Realm
                className="org.jboss.web.tomcat.security.JBossSecurityMgrRealm" 
                certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping"/>
            <Logger className="org.jboss.web.tomcat.Log4jLogger"
                verbosityLevel="WARNING" category="org.jboss.web.localhost.Engine"/>
            <Host name="localhost" autoDeploy="false" deployOnStartup="false" 
                  deployXML="false">
                <DefaultContext cookies="true" crossContext="true" override="true"/>
            </Host>
        </Engine>
    </Service>
</Server>

이 설정의 빠른 테스트는 https://localhost/jmx-console/index.jsp 라는 URL을 사용해서 JMX 콘솔 웹 어플리케이션에 액세스해보는 것입니다.

참고: 여러분이 유닉스계열의 시스템(Linux, Solaris, OS X)에서 테스트한다면, 운영체제에서 1024 포트 아래쪽 포트를 접속하기 위해서는 root 계정이 필요하기 때문에 그러한 권한을 획득할 수 없다면 8443과 같은 포트 번호로 변경해주여야 합니다.

팩토리 설정 속성들은 다음과 같습니다:

  • algorithm: 사용할 인증서 인코딩 알고리즘. 지정하지 않으면, 기본값은 SunX509가 됩니다.
  • className: SSL 서버 소켓 팩토리 구현 클래스의 완전한 형태를 갖는 클래스의 이름. 여러분은 이곳에 반드시 org.apache.coyote.tomcat4.CoyoteServerSocketFactory 를 지정해주셔야만 합니다. 다른 소켓 팩토리를 사용할 경우에도 오류는 발생되지 않지만, 서버 소켓은 SSL을 사용하지 않게 됩니다.
  • clientAuth: 연결을 수락하기전에 클라이언트로부터 올바른 인증서 체인을 요청하는 SSL 스택을 원할 때는 true로 설정합니다. false 값(기본값임)인 경우, CLIENT-CERT 인증을 사용하는 보안 제약에 의해 보호되는 리소르를 클라이언트가 요청하지 않는 한, 인증서 체인을 요청하지 않습니다.
  • keystoreFile: 키저장(keystore) 파일의 경로로, 로딩시키는 서버 인증서가 저장되어진 곳이 됩니다. 기본적으로 경로명은 톰캣을 실행시키는 사용자의 운영 체제의 홈 디렉터리내의 ".keystore" 파일이 됩니다.
  • keystorePass: 지정한 키저장 파일로부터 서버 인증서에 액세스할 때 사용하는 암호입니다. 기본값은 "changeit" 입니다.
  • keystoreType: 서버 인증서로 사용되는 키저장 파일의 타입입니다. 기본값은 "JKS" 입니다.
  • protocol: 사용할 SSL 프로토콜의 버전입니다. 지정하지 않으면, 기본값인 "TLS"이 됩니다.

만일 여러분이 8 장, JBoss에서의 보안 chap8.keystore에서 논의했던 자기-서명된 인증서를 사용하여 이 설정을 테스트해보고자 한다면, 여러분의 브라우저에서는 여러분이 연결하려는 서버쪽의 인증서 서명이 신뢰할 수 있는 기관에 의해 발급되지 않았다는 경고창을 보여줄 것입니다. 즉, 첫 번째 설정 예제를 테스트하게 되면, IE 5.5에서는 그림 9.3, “인터넷 익스플로어 5.5의 보안 경고창”에서 보여지는 것과 유사한 화면이 나타납니다. 그림 9.4, “인터넷 익스플로어 5.5의 SSL 인정서 상세정보창”은 서버 인증서의 세부정보를 보여줍니다. 이러한 경고는 임의의 정보를 자기-서명한 인증서를 통해 서비스할 때 중요합니다. 이러한 경고창을 없애기 위한 유일한 방법은 신뢰된 제 3의 기관으로부터 인증받은 서명을 통해 만들어진 인증서를 사용하는 것입니다. 다른쪽 시스템에서 검증할 수 있는 유일한 방법은 신뢰된 제 3의 기관에 의해 서명된 것을 사용하여 검증하는 것입니다.

인터넷 익스플로어 5.5의 보안 경고창.

그림 9.3. 인터넷 익스플로어 5.5의 보안 경고창.

인터넷 익스플로어 5.5의 SSL 인정서 상세정보창.

그림 9.4. 인터넷 익스플로어 5.5의 SSL 인정서 상세정보창.

9.2.3. 가상 호스트의 설정

가상 호스트를 통해 여러분은 JBoss를 통해 다양한 DNS 이름을 갖는 웹 어플리케이션을 그룹화 할 수 있습니다. 예를 들어, 예제 9.5, “가상 호스트 설정 예제”에서 주어진 server.xml 설정 파일을 생각해보겠습니다. 이 설정에서는 vhost1.mydot.com이라는 기본 호스트 이름을 정의하고, 두 번째 호스트 이름으로 vhost2.mydot.com을 정의하고 이와 연결된 별칭으로 www.mydot.com을 정의했습니다.

예제 9.5. 가상 호스트 설정 예제.

<Server>
   <Service name="jboss.web"
      className="org.jboss.web.tomcat.tc5.StandardService">
       
      <!-- A HTTP/1.1 Connector on port 8080 -->
      <Connector port="8080" address="${jboss.bind.address}"
                 maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
                 enableLookups="false" redirectPort="8443" acceptCount="100"
                 connectionTimeout="20000" disableUploadTimeout="true"/>

      <Engine name="jboss.web" defaultHost="vhost1">
         <Realm className="org.jboss.web.tomcat.security.JBossSecurityMgrRealm"
                certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping"
            />
         <Logger className="org.jboss.web.tomcat.Log4jLogger"
                 verbosityLevel="WARNING"
                 category="org.jboss.web.localhost.Engine"/>

            <Host name="vhost1" autoDeploy="false"
                  deployOnStartup="false" deployXML="false">
                <Alias>vhost1.mydot.com</Alias>
                <Valve className="org.apache.catalina.valves.AccessLogValve"
                       prefix="vhost1" suffix=".log" pattern="common"
                       directory="${jboss.server.home.dir}/log"/>
            
  
                <DefaultContext cookies="true" crossContext="true" override="true"/>
            </Host>   
            <Host name="vhost2" autoDeploy="false" 
                  deployOnStartup="false" deployXML="false">
                <Alias>vhost2.mydot.com</Alias>
                <Alias>www.mydot.com</Alias>  

                <Valve className="org.apache.catalina.valves.AccessLogValve"
                       prefix="vhost2" suffix=".log" pattern="common" 
                       directory="${jboss.server.home.dir}/log"/>

                <DefaultContext cookies="true" crossContext="true" override="true"/>
            </Host>
      </Engine>
   </Service>
</Server>

WAR 파일을 배치하게 되면, 이것은 기본적으로는 포함하고 있는 EnginedefaultHost 속성에 일치하는 이름을 갖는 가상 호스트에 연결됩니다. 여러분이 jboss-web.xml 서술자내에 적절한 virtual-host 정의를 지정해주면 지정된 가상 호스트쪽으로 WAR가 배치됩니다. 다음에 오는 jboss-web.xml 서술자에서는 WAR를 www.mydot.com라는 가상 호스트에 어떻게 배치시키는지를 보여주고 있습니다. 설정 파일내에 가상 호스트의 이름과 실제 호스트의 이름중 하나를 사용해 줄 수 있다는 것을 참고하십시오.

<jboss-web>
    <context-root>/</context-root>
    <virtual-host>www.mydot.com</virtual-host>
</jboss-web>

9.2.4. 정적인 컨텐츠 서비스하기

JBoss는 root 어플리케이션 컨텍스트를 위한 컨텐츠 서비스를 하는 기본 어플리케이션을 제공합니다. 이 기본 컨텍스트는 jbossweb-tomcat50.sar 디렉터리내의 ROOT.war 어플리케이션입니다. 여러분은 다른 어플리케이션과 관계가 없는 정적인 파일을 ROOT.war 디렉터리에 추가시켜 서비스할 수 있습니다. 즉, 공유되는 이미지 디렉터리를 만들고 싶다면, ROOT.war 디렉터리에 image 라는 서브디렉터리를 만들고 그곳에 이미지들을 위치시켜 놓으십시오. 이 디렉터리에 들어가있는 myimage.jpg라는 이름의 이미지는 http://localhost:8080/images/myimage.jpg라는 URL로 접근할 수 있습니다.

9.2.5. 톰캣을 아파치와 함께 사용하기

몇몇 아키텍쳐의 경우, JBoss 서버의 앞단에 아파치 웹 서버를 내세우는 것이 좋을 수도 있습니다. 외부의 웹 클라이언트들은 아파치 인스턴스와 대화를 하게 되고, 그 다음 차례에 톰캣 인스턴스와 통신을 하게 됩니다. 아파치에서는 톰캣내에서 동작하는 AJP 커넥터에 AJP 프로토콜을 사용해 통신하는 mod_jk 모듈을 사용할 수 있도록 설정해주어야만 합니다. 제공되는 server.xml 파일에는 이 AJP 커넥터를 사용할 수 있도록 되어있습니다.

<Connector port="8009" address="${jboss.bind.address}"
           enableLookups="false" redirectPort="8443" debug="0"
           protocol="AJP/1.3" />

완전한 설치 방법은 아파치와 mod_jk 문서를 참조해야 합니다. 여러분이 아파치 인스턴스를 제대로 설정했다면, 다음 설정의 일부분에서 어떻게 컨텍스트의 루트가 /jbosstest인 배치된 WAR에 연결시키는 지를 보여주고 있습니다.

...
LoadModule jk_module libexec/mod_jk.so
AddModule mod_jk.c
           
<IfModule mod_jk.c>
    JkWorkersFile /tmp/workers.properties
    JkLogFile /tmp/mod_jk.log
    JkLogLevel debug
    JkMount /jbosstest/* ajp13
</IfModule>

workers.properties 파일에는 JBoss 인스턴스에 어떻게 연결하는지에 대한 상세한 설정이 담겨있습니다.

9.2.6. 클러스터링 사용하기

JBoss는 내장된 톰캣 서비스내에서 클러스터링을 지원합니다. 내장된 톰캣 컨테이너의 클러스터링 설정 단계는 다음과 같습니다:

  • 로드밸런서를 사용한다면, 스티키(sticky) 세션을 사용하는 설정이 되어있는지 확인합니다. 이것은 어느 한 사용자가 A 노드에서 세션을 시작하였다면, 다음부터 오는 일련의 요청들은 A 노드가 살아있는동안은 계속 A노드로만 보내어지게 한다는 것입니다. 아파치 웹서버의 스티키 세션 설정의 자세한 사항은 다음 사이트를 참고하십시오: http://www.okjsp.pe.kr/
  • 여러분이 JBoss의 all 설정을 사용하지 않을 경우, 여러분의 deploy 디렉터리내에 cluster-service.xml 파일을 넣어주십시오. 이 파일은 server/all/deploy 디렉터리에서 찾을 수 있습니다. 또한 여러분은 여러분의 lib 디렉터리내에 jgroups.jar도 넣어주셔야만 합니다. 이 파일은 server/all/lib 디렉터리에서 찾을 수 있습니다.
  • 여러분의 설정이 정상적으로 동작하는지 JBoss를 시작하면서 점검해보십시오. JMX 관리 콘솔(http://localhost:8080/jmx-console/)에서 jboss.cache:service=TomcatClusteringCache MBean 을 찾으십시오. StateString 이 반드시 Started 되어야만 합니다. 이 값이 Stopped 이라면, 서버의 로그 파일을 살펴보십시오.
  • 웹 어플리케이션의 클러스터링을 가능케 하려면, 여러분은 web.xml 서술자내에서 distributable로 표시해주셔야만 합니다. 즉 :
    <?xml version="1.0"?> 
    <!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd ">
    <web-app>
        <distributable/>
        <!-- ... -->
    </web-app>
    
  • 보통의 경우처럼 여러분의 WAR를 배치하면 이제 클러스터링 됩니다.

여러분이 어플리케이션을 배치하고 액세스하였다면, jboss.cache:service=TomcatClusteringCache MBean을 JMX 콘솔에서 선택하고 printDetails 오퍼레이션을 호출해보십시오. 다음과 유사한 결과를 보실 수 있습니다.

/JSESSION

/n6HywRwITbY-xvzaZ0LS5Q**
n6HywRwITbY-xvzaZ0LS5Q**: org.jboss.invocation.MarshalledValue@9c1dddab

/R1T4Dapn7c8T-+Ynd9v9MA**
R1T4Dapn7c8T-+Ynd9v9MA**: org.jboss.invocation.MarshalledValue@8c0f60b6

위의 결과는 두 개의 별도 웹 세션이 JBossCache를 통해 공유되었다는 것을 보여줍니다. 만약 어떠한 결과도 나타나지 않았다면, 어플리케이션이 제대로 distributable 표시되지 않았거나 HTTP 세션내에 위치한 값을 갖는 어플리케이션의 부분에 접속하지 않았기 때문입니다.